我们首先来看看等待队列的组成。
等待队列由等待队列头和等待队列项组成。要看的等待队列先要去linux-2.6.35/include/wait.h 。
我们来看看等待队列的头的样子:
Wait_queue_head_t就是等待队列的头,他的原型是
Typedef __wait_queue_head wait_queue_head_t
Struct __wait_queue_head{
Spinlock_t lock;
Struct list_head task_list;
};
关键字:task_list
Struct list_head{
Struct list_head *next *prev
};
由此可见这个结构体里面只有两个指针,两个指针都是指向struct list_head 的结构体指针。
关键字:lock
Lock是spinlock_t类型的变量,只是一种锁而已。
看完了等待队列头再来看看等待队列项。等待队列项是在内核中的Linux-2.6.35/include/linux/wait.h
wait_queue_t就是等待队列的类型,他的原型是
Typedef struct __wait_queue wait_queue_t
Struct __wait_queue{
Unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
Void *private;
Wait_queue_fun_t func;
Struct list_head task_list;
};
在观察等待队列的项的结构体时就会发现有一项struct list_head跟等待队列头的时相同的,因此我们可以想象下,等待队列头跟等待队列项是通过这个结构体来连接的,等待队列项跟其他的等待队列项也是通过这个结构体来连接的。
那么其他成员代表什么意思呢??
先去看flags,看名字就知道是标识作用,该标志是判断进程是否是互斥进程。总是唤醒所有等待事件的进程并不一定是合适的。如果队列中的多个进程等待资源是要互斥访问的,一定时间内只允许一个进程去访问,这时候只需要唤醒一个。
我们再来看一个比较奇怪的成员void *private。
从表面上是看不出这个成员的作用。其实在2.6版本中采用的是void指针,而以前的版本是struct task_struct *task;在实际用的时候还是把private赋值给task。在本文件也就是linux-2.6.35/include/liunx/wait.h会有赋值的体现。
Static inline void(wait_queue_t *q,struct task_struct *p)
{
q->flags = 0;
q->private = p;
q->func = default_wake_function;
}
在这个函数中又出现了等待队列项中的参数func成员的赋值。
那么task_struct这个结构体时什么呢,我觉得看着很眼熟,其实就是很眼熟,它就是进程控制块PCB,里面有进程的所有信息。每一个进程都会有唯一的结构体。它里面包含着进程状态的标志位就是结构体的第一个成员叫volatile long state;改变这个值可以改变进程的状态,包括可执行,可中断等待,不可中断等待等。
至于default_wake_function 就是唤醒函数。
说完了等待队列头跟等待队列项。那么他们是怎么连接在一起的,内核又提供了那些函数来实现的。
有一个函数叫add_wait_queue的函数在 linux-2.6.35/kernel/wait.c下实现的。
它的作用就是把等待队列头与等待队列项关联起来形成一个链表,如下图:
它的函数原型是 void add_wait_queue(wait_queue_head_t *q,Wait_queue_t *wait)
{
Unsigned long flags;
Wait->flags &= ~WQ_FLAG_EXCLUSIVE;
Sipn_lock_irqsave(&q->lock,flags);
__add_wait_queue(q,wait);
Spin_unlock_irqrestore(&q->lock,flags);
}
看这个函数很容易就看到__add_wait_queue这函数才是整个函数实现的关键,其他函数都是来帮助这个函数的实现。
__add_wait_queue(wait_queue_head_t head,wait_queue_t new)
调用 list_add(&new->task_list,&head->task_list)
调用__list_add(new,head,head->next)
我们看一下__list_add的实现
Void __list_add(struct list_head *new, struct list_head *prevStruct list_head *next)
{
Next -> prev = new;
New -> next = next;
New -> prev = prev;
Prev -> next = new;
}
这个函数的实现是什么呢?
头插法,就是头插法实现了等待队列的添加。
转自:http://www.embedu.org/Column/Column570.htm