Wait queues

来源:互联网 发布:nba2k14数据修改器 编辑:程序博客网 时间:2024/06/06 01:16

3.2.4. How Processes Are Organized
The runqueue lists group all processes in a TASK_RUNNING state. When it comes to grouping processes in other states, the various states call for different types of treatment, with Linux opting for one of the choices shown in the following list.

  • Processes in a TASK_STOPPED, EXIT_ZOMBIE, orEXIT_DEAD state are not linked in specific lists. There is no need to group processes in any of these three states, because stopped, zombie, and dead processes are accessed only via PID or via linked lists of the child processes for a particular parent.

  • Processes in a TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE state are subdivided into many classes, each of which corresponds to a specific event.In this case, the process state does not provide enough information to retrieve the process quickly, so it is necessary to introduce additional lists of processes. These are calledwait queues and are discussed next.

3.2.4.1. Wait queues

Wait queues have several uses in the kernel, particularlyfor interrupt handling, process synchronization, and timing.Because these topics are discussed in later chapters, we'll just say here that a process must often wait for some event to occur, such as for a disk operation to terminate, a system resource to be released, or a fixed interval of time to elapse. Wait queues implement conditional waits on events: a process wishing to wait for a specific event places itself in the proper wait queue and relinquishes control. Therefore, a wait queue represents a set of sleeping processes, which are woken up by the kernel when some condition becomes true.

Wait queues are implemented as doubly linked lists whose elements include pointers to process descriptors. Each wait queue is identified by await queue head, a data structure of type wait_queue_head_t:

    struct _ _wait_queue_head {        spinlock_t lock;        struct list_head task_list;    };    typedef struct _ _wait_queue_head wait_queue_head_t;

Because wait queues are modified by interrupt handlers as well as by major kernel functions, the doubly linked lists must be protected from concurrent accesses, which could induce unpredictable results (seeChapter 5). Synchronization is achieved by thelock spin lock in the wait queue head. The task_list field is the head of the list of waiting processes.

Elements of a wait queue list are of typewait_queue_t:

   struct _ _wait_queue {        unsigned int flags;        struct task_struct * task;        wait_queue_func_t func;        struct list_head task_list;    };    typedef struct _ _wait_queue wait_queue_t;

Each element in the wait queue list represents a sleeping process, which is waiting for some event to occur; its descriptor address is stored in thetask field. The task_list field contains the pointers that link this element to the list of processes waiting for the same event.

However, it is not always convenient to wake upall sleeping processes in a wait queue. For instance,if two or more processes are waiting for exclusive access to some resource to be released, it makes sense to wake up just one process in the wait queue.This process takes the resource, while the other processes continue to sleep. (This avoids a problem known as the "thundering herd," with which multiple processes are wakened only to race for a resource that can be accessed by one of them, with the result that remaining processes must once more be put back to sleep.)

Thus, there are two kinds of sleeping processes:exclusive processes(denoted by the value 1 in theflags field of the corresponding wait queue element) are selectively woken up by the kernel, whilenonexclusive processes (denoted by the value 0 in theflags field) are always woken up by the kernel when the event occurs. A process waiting for a resource that can be granted to just one process at a time is a typical exclusive process. Processes waiting for an event that may concern any of them are nonexclusive. Consider, for instance, a group of processes that are waiting for the termination of a group of disk block transfers: as soon as the transfers complete, all waiting processes must be woken up. As we'll see next, thefunc field of a wait queue element is used to specify how the processes sleeping in the wait queue should be woken up.



内核中每个等待队列都是为一个他等待的资源而存在的,(更新中...)
1.驱动中可以显式定义一个队列wait_queue_head_t(即队列头,唯一的表示一个队列),然后用wait_event_interruptible()等将当前进程加入等待队列
2.有时不仅要定义一个wait_queue_head_t,还要手动定义一个wait_queue_t,比如在调用prepare_to_wait()等时
3.驱动中调用down_interruptible(*sem)去获取一个semphore时,如果semphore不可用,则系统自动创建并维护一个等待相关信号量的等待队列,然后将进程加入队列

内核函数中的阻塞实例
1.进程调用open()和read()和write()时,在资源not in place时,驱动中对应的open()和read()和write()需要调用wait_event_interruptible()等,才能实现阻塞型io
2.进程调用select数据not in place时,进程被塞进在驱动中对应的poll方法中的poll_wait指定的等待队列,poll不执行阻塞进程的动作,而是do_select()将进程状态设置为interruptible,将进程塞进等待队列,并schedule())
3.进程调用sleep(),则内核自动为其创建并维护一个等待队列
原创粉丝点击