Before we look at the blocking sleep implementation mechanism, let's look at the waiting queues that are widely used in the kernel.
The waiting queue of the Linux kernel is a double-loop linked list structure, which is closely integrated with the process scheduling mechanism and can be used to implement the core asynchronous event notification mechanism. It has two data structures: the wait queue header (wait_queue_head_t) and the Wait Queue entry (wait_queue_t). Both the wait queue header and the Wait queue item contain a list_head (doubly linked list). The waiting process is linked by such a doubly linked list.
Here's a look at both data structures:
struct __wait_queue_head { //自旋锁,实现对等待队列的互斥访问 struct//双向循环链表,存放等待的进程。};typedefstruct __wait_queue_head wait_queue_head_t;struct __wait_queue { unsignedint flags; void *private; wait_queue_func_t func; struct list_head task_list;};typedefstruct __wait_queue wait_queue_t;
We know that the IO is blocked by default (unless set O_nonbloc), and if a process calls read but no data is available (not yet), the process must be blocked. This process is immediately awakened when data is reached, and that data is returned to the caller, even if it is less than the number requested in the count parameter to the method, and if a process calls write and there is no space in the buffer, the process must be blocked and it must be in one with the read In a different wait queue. When some data is written to the hardware device and the space in the output buffer becomes idle, the process is woken up and the write call succeeds, although the data may only be partially written if there is only no space in the buffer to give the requested count bytes.
Let's look at an example of a read operation:
Static ssize_t scull_p_read (struct file*Filp, Char __user*BUF, size_t count, loff_t*F_pos) {struct scull_pipe*Dev=Filp -Private_data;if(Down_interruptible (&Dev -SEM))//Locking return -erestartsys; while(Dev -Rp==Dev -wp//No things to read{Up (&Dev -SEM);//Unlock if(Filp -F_flags&O_nonblock)//non-blocking mode, return immediately return -eagain;//block access, sleep wait, wait until the read condition is met to continue execution. if(Wait_event_interruptible (Dev -Inq, (Dev -Rp!=Dev -WP)))return -erestartsys;if(Down_interruptible (&Dev -SEM))//re-lock return -erestartsys; }//Read data. ... up (&Dev -SEM); Wake_up_interruptible (&Dev -OUTQ);returnCount;}
From the above example, we can see that the blocking wait is implemented by calling Wait_event_interruptible (), and look at the wait_event_interruptible () implementation
#define Wait_event_interruptible (Wq,condition) ({int__ret =0;if( (!condition)) __wait_event_interruptible (Wq,condition, __ret); __ret; }) #define __wait_event_interruptible (Wq,condition, __ret) Do{define_wait (__wait);//define wait queue __wait for(;;) {prepare_to_wait (&wq, &__wait, task_interruptible);//wait queue __wait join in Wq-led queue list, and set the process state to task_interruptible if(condition)//If condition is satisfied then jump out Break;if(!signal_pending (current)) {//Not awakened by the signalSchedule ();//Discard CPU, dispatch other process execution Continue; } ret =-Erestartsys; Break; } finish_wait (&wq, &__wait);//The wait queue __wait is removed from the waiting queue list that the waiting queue header Wq points to, and the process state is set to Task_running} while(0)
To summarize, the blocking sleep steps are generally:
1) Assign and initialize a wait_queue_t structure, and then add it to the correct wait queue. When everything is in place, the person responsible for waking up the work can find the right process.
2) Set the status of the process to flag it for sleep (task_uninterruptible,task_interruptible)
3) Call schedule () and let the CPU out.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Blocking sleep Implementation Mechanism