原文链接:http://blog.csdn.net/wangchaoxjtuse/archive/2010/12/02/6049500.aspx
关于linux等待队列,大体有如下描述:
等待队列是一个双向循环链表,在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。
等待队列头和等待队列项中都包含一个list_head类型的域作为"连接件"。
等待对头wait_queue_head_t 和等待队列元素wait_queue_t的定义如下:
//队头结构
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
//队列元素结构
struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
从队头和队列元素的定义中,可以看出,都包含了list_head类型的结构,而这个结构中只保存了向前和向后的指针。定义如下。
struct list_head {
struct list_head *next, *prev;
};
看到这里就有些纳闷了,为什么这些方向指针和外面的结构体是分开的,也就是说,实际的指向关系只是这些方向指针之间的关系,而其他元素是附加在这些指针之上的。和我们传统的数据结构中的链表定义有很大的不同。
由此,又产生了一个疑问:那通过这些指针来获得具体的队列元素属性的时候,岂不是很不方便?(需要通过结构体某个成员的地址来计算该结构体的的地址?联想到之前学校的函数container_of或许可以完成此功能)
好奇心驱使下,最终了解了内核中队列的wakeup函数的实现,果不其然,在list_entry的定义中,找到了container_of的调用。
具体调用关系如下:
__wake_up_common--》list_for_each_entry_safe——》list_entry——》container_of
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int wake_flags, void *key)
{
wait_queue_t *curr, *next;
list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
unsigned flags = curr->flags;
if (curr->func(curr, mode, wake_flags, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
#define list_for_each_entry_safe(pos, n, head, member) /
for (pos = list_entry((head)->next, typeof(*pos), member), /
n = list_entry(pos->member.next, typeof(*pos), member); /
&pos->member != (head); /
pos = n, n = list_entry(n->member.next, typeof(*n), member))
#define list_entry(ptr, type, member) /
container_of(ptr, type, member)