How Linux workqueue works "Go"

Source: Internet
Author: User

Transferred from: http://blog.chinaunix.net/uid-21977330-id-3754719.html

Transferred from: http://bgutech.blog.163.com/blog/static/18261124320116181119889/
1. What is Workqueue
The workqueue mechanism in Linux is designed to simplify the creation of kernel threads. The kernel thread can be created by invoking the Workqueue interface. and the number of threads can be created based on the number of current system CPUs, so that threading transactions can be parallelized. Workqueue is a simple and effective mechanism in the kernel, and he clearly simplifies the creation of kernel daemon and facilitates user programming.

The Work queue (Workqueue) is another form of delaying work. The work queue can be pushed back into a kernel thread to execute, that is, the lower part can be executed in the context of the process. The most important thing is that the work queue is allowed to be re-dispatched or even asleep.
So, under what circumstances use the work queue, and under what circumstances use Tasklet. If the deferred task requires sleep, select the work queue. If the deferred task does not require sleep, then select Tasklet. In addition, if you need to use an entity that can be re-dispatched to perform your next-half processing, you should also work with the task queue. It is the only mechanism that can be implemented in the bottom half of the process context, and only it can sleep. This means that when you need to get a lot of memory, when you need to get a semaphore, it can be very useful when you need to perform blocking I/O operations. If you do not need to use a kernel thread to postpone your work, consider using Tasklet.

2. Data structure
We call the deferred task work, describing its data structure as work_struct:

struct Work_struct {    atomic_long_t data;       /* Parameters of Func for working handler */#define WORK_STRUCT_PENDING 0/        * T If Work item PENDING execution */#define Work_struct_static 1
   
    /* static initializer (debugobjects) */#define WORK_STRUCT_FLAG_MASK (3UL) #define WORK_STRUCT_WQ_DATA_MASK (~work_ Struct_flag_mask)    struct list_head entry;        /* Pointer to work/*    work_func_t func;              /* Work handler function */#ifdef CONFIG_LOCKDEP    struct lockdep_map lockdep_map; #endif};
   

The work is organized into work queues (workqueue) with a structure of workqueue_struct:

struct Workqueue_struct {struct cpu_workqueue_struct *cpu_wq; struct list_head list; const char *name;   /*workqueue name*/int singlethread;   /* is not a single thread-single thread Our preferred first CPU-0 represents the default worker thread event*/int freezeable;  


In the case of multi-threading, Linux creates cpu_workqueue_struct based on the number of current system CPUs:

Truct cpu_workqueue_struct {spinlock_t lock;/* because the worker thread needs to handle the work that is connected to it frequently, it needs the yoke protection */struct List_head worklist; wait_queue _head_t more_work; struct Work_struct *current_work; /* Current work*/struct workqueue_struct *wq;   /* belongs to the workqueue*/struct task_struct *thread; /* Context of the task */} ____cacheline_aligned;

In this structure the main maintenance of a task queue, as well as kernel threads need to sleep waiting queue, in addition to maintain a task context, namely task_struct.
The relationship between the three is as follows:

3. Create a job
3.1 Creating a Work queue
A. Create_singlethread_workqueue (name)
As shown in the implementation mechanism of this function, the function returns a pointer variable of type struct workqueue_struct, where the memory address pointed to by the pointer variable is called KZALLOC dynamically generated inside the function. So driver is called when the work queue is no longer used:

void Destroy_workqueue (struct workqueue_struct *wq) to free the memory address here.

The CWQ in the figure is a PER-CPU type of address space. For Create_singlethread_workqueue, even for multi-CPU systems, the kernel is only responsible for creating a worker_thread kernel process. After the kernel process is created, the wait node in the diagram is defined first, then the worklist in the CWQ is checked in a loop body, and if the queue is empty, the wait node is added to More_work in Cwq and then dormant in the wait queue.

Driver calls Queue_work (struct workqueue_struct *wq, struct work_struct *work) to join the work node to the Wq. Work is added to the linked list that cwq->worklist points to. Queue_work joins a work node to the cwq->worklist and calls Wake_up to wake the Worker_thread process dormant on the cwq->more_work. WAKE_UP calls the Autoremove_wake_function function on the wait node and then removes the wait node from Cwq->more_work.

Worker_thread is dispatched again to begin processing all work nodes in the cwq->worklist ... When all the work nodes are processed, Worker_thread re-joins the wait node to Cwq->more_work and then sleeps again in the wait queue until driver calls Queue_work ...

B. Create_workqueue

Create_workqueue also assigns a WQ work queue relative to Create_singlethread_workqueue, but the difference is that for a multi-CPU system, a per-is created for each CPU. The CWQ structure of the CPU, corresponding to each CWQ, will generate a new worker_thread process. But when the work node is submitted to CWQ with Queue_work, which CPU calls the function, it adds the work node to the worklist on the CWQ corresponding to the CPU.

C. Summary
When the user calls Workqueue's initialization interface Create_workqueue or Create_singlethread_workqueue initializes the Workqueue queue, the kernel begins assigning a Workqueue object to the user. And chain it to a global workqueue queue. Linux then allocates a Cpu_workqueue_struct object with the same number of CPUs for the Workqueue object based on the current CPU, and each Cpu_workqueue_struct object has a task queue. Next, Linux allocates a kernel thread for each Cpu_workqueue_struct object, which is the kernel daemon to handle the tasks in each queue. At this point, the user invokes the initialization interface to complete the Workqueue initialization and returns the Workqueue pointer.

Once the workqueue is initialized, the context of the task run is built, but there is no executable task, so you need to define a specific Work_struct object. Then add the work_struct to the task queue, and Linux wakes up the daemon to handle the task.

The Workqueue kernel implementation principle described above can be described as follows:

3.2 Creating a Job
To use a work queue, the first thing to do is to create some work that needs to be pushed backwards. The structure can be statically built at compile time by Declare_work:
Declare_work (Name,void (*func) (void *), void *data);
This will statically create a work_struct structure named name, the function to execute is func, and the parameter is data.
Similarly, you can create a job with pointers at run time:
Init_work (Structwork_struct *work, Woid (*func) (void *), void *data);

4. Scheduling
A. Schedule_work

In most cases, it is not necessary to establish a work queue yourself, but to define work, to hook the work structure to the kernel's predefined event work queue, and to define a static global amount of work queue static struct Workqueue_ in kernel/workqueue.c struct *KEVENTD_WQ; The default worker thread is called events/n, where n is the number of the processor, and each processor corresponds to a thread. For example, a single-processor system only events/0 such a thread. The dual-processor system will have one more EVENTS/1 thread.
Schedule the work structure, add the work structure to the global event work queue Keventd_wq, and call the Queue_work generic module. The Keventd_wq interface is shielded from the outside, and the user does not need to know this parameter, which is equivalent to using default parameters. KEVENTD_WQ is maintained by the kernel itself, created, destroyed. The work will be dispatched immediately, and it will be executed once the worker thread on the processor on which it is located is awakened.

B. schedule_delayed_work (&work,delay);
Sometimes you don't want your work to be executed right away, but hopefully it will be executed after a delay. In this case, the timer can also be used to schedule the delay, after the expiration of the default timer callback function to register the work. After delaying delay, it is awakened by the timer and the work is added to the working queue Wq.

The work queue has no priority and is processed basically in a FIFO manner.

5. Work Queue API

1. Create_workqueue is used to create a workqueue queue that creates a kernel thread for each CPU in the system. Input parameters:

@name: Name of Workqueue

2. Create_singlethread_workqueue is used to create workqueue, creating only one kernel thread. Input parameters:

@name: Workqueue Name

3. Destroy_workqueue release the Workqueue queue. Input parameters:

@ workqueue_struct: Workqueue queue pointer that needs to be freed

4. Schedule_work scheduling performs a specific task, and the tasks performed will be mounted into the WORKQUEUE--KEVENTD_WQ input parameters provided by the Linux system:

@ work_struct: Specific Task object pointer

5. Schedule_delayed_work delay a certain time to perform a specific task, the function is similar to the schedule_work, one more delay time, input parameters:

@work_struct: Specific Task object pointers

@delay: Delay Time

6. The Queue_work schedule executes a task in a specified workqueue. Input parameters:

@ workqueue_struct: The specified workqueue pointer

@work_struct: Specific Task object pointers

7. Queue_delayed_work delay scheduling performs a task in a specified workqueue, similar to queue_work, with one delay for input parameters.

6. Example


  1. Statement
  2. static struct workqueue_struct *mdp_pipe_ctrl_wq; /* MDP MDP Pipe CTRL WQ */
  3. static struct delayed_work mdp_pipe_ctrl_worker;
  4. Mdp_pipe_ctrl_wq = Create_singlethread_workqueue ("Mdp_pipe_ctrl_wq");//Create Work queue
  5. Init_delayed_work (&mdp_pipe_ctrl_worker,mdp_pipe_ctrl_workqueue_handler);//delayed_work is bound to Task_func.
  6. Processing functions
  7. static void Mdp_pipe_ctrl_workqueue_handler (struct work_struct *work)
  8. {
  9. Mdp_pipe_ctrl (Mdp_master_block, Mdp_block_power_off, FALSE);
  10. }
  11. Starts calling the work queue, and the delay time is the execution of the handler function.
  12. unsigned long mdp_timer_duration = (HZ/20); /* Msecond */
  13. /* Send Workqueue to turn off MDP power */
  14. Queue_delayed_work (Mdp_pipe_ctrl_wq,&mdp_pipe_ctrl_worker, mdp_timer_duration);
  15. /* Cancel Pipe CTRL worker */
  16. Cancel_delayed_work (&mdp_pipe_ctrl_worker);
  17. /* for Workder can ' t be cancelled ... */
  18. Flush_workqueue (MDP_PIPE_CTRL_WQ);
  19. /* for Workder can ' t be cancelled ... */
  20. Flush_workqueue (MDP_PIPE_CTRL_WQ);

Many of the scenarios in the driver program need to be deferred, so that the work queue is good for us.

How Linux workqueue works "Go"

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.