本文最后更新于 26 天前,其中的信息可能已经有所发展或是发生改变。
概述
Linux内核的等待队列(Wait Queue)是重要的数据结构,与进程调度机制紧密相关联,可以用来同步对系统资源的访问、异步事件通知、跨进程通信等。
在Linux中,等待队列以循环链表为基础结构,包括两种数据结构:等待队列头(wait queue head)和等待队列元素(wait queue),整个等待队列由等待队列头进行管理
核心原理
内核等待队列的实现依靠的是对于进程状态的管理和任务调度队列的管理来实现任务的唤醒和休眠
需要注意的是等待队列中的任务并不与线程一一对应,这个等待者可以对应多种类型,包括线程、进程、文件/poll/epoll回调型等待者
核心数据结构
内核中为等待队列定义了一个核心数据结构
//linux-5.4/include/linux/wait.h
struct wait_queue_entry {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head entry;
};
struct wait_queue_head {
spinlock_t lock;
struct list_head head;
};
typedef struct wait_queue_head wait_queue_head_t;
关键API
初始化
创建等待队列时会调用DECLARE_WAITQUEUE这个宏来创建并初始化一个等待队列节点
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.entry = { NULL, NULL } }
#define DECLARE_WAITQUEUE(name, tsk) \
struct wait_queue_entry name = __WAITQUEUE_INITIALIZER(name, tsk)
这里的default_wake_function函数会在__wake_up_common函数中被调用执行
static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
int nr_exclusive, int wake_flags, void *key,
wait_queue_entry_t *bookmark)
{
// ...
list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {
// ...
ret = curr->func(curr, mode, wake_flags, key);
// ...
}
// ...
return nr_exclusive;
}
新增节点
调用add_wait_queue函数将创建的节点添加到等待队列中,在下一次时钟中断,进程切换时任务调度器会查看就绪队列中的进程
需要注意的是进程调度器用来管理进程的数据结构是runqueue,不是等待队列
主动让出CPU
在将节点添加到等待队列后需要手动调用schedule()函数来让出CPU,因为只是将任务添加到等待中不会阻塞任务。这里主动让出CPU才会在下一次进程调度中将自身挂起
删除节点
调用remove_wait_queue函数将创建的节点从等待队列中删除