wait queue
在内核中,一般使用等待队列(wait queue)
实现当发生特定事件时的异步通知
具体方法如下:
1. 为每个特定的事件(可读、可写等)维护一个等待队列,本质上是一个双向链表
2. 关心该事件的实体作为等待队列中一项加入该等待队列中
3. 每个等待队列项包含关注该事件的实体的信息(进程、线程),以及回调函数
4. 当关注的事件发生时,会遍历相应的等待队列,调用每个节点中指定的回调函数
1. Structure
// wait_queue 头节点
typedef struct __wait_queue_head wait_queue_head_t;
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
// wait_queue 节点
typedef struct __wait_queue wait_queue_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 int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
2. Init
// include/linux/wait.h
extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
// /kernel/sched/wati.c
void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
{
spin_lock_init(&q->lock);
lockdep_set_class_and_name(&q->lock, key, name);
INIT_LIST_HEAD(&q->task_list);
}
// include/linux/list.h
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
// include/linux/wait.h
static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
{
q->flags = 0;
q->private = p;
q->func = default_wake_function;
}
// include/linux/wait.h
static inline void
init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func)
{
q->flags = 0;
q->private = NULL;
q->func = func;
}