Linux内核的休眠与唤醒机制
在Linux内核中存在着等待队列的数据结构,该数据结构是基于双端链表实现,Linux内核通过将阻塞的进程任务添加到等待队列中,而进程任务被唤醒则是在队列轮询遍历检测是否处于就绪状态,如果是那么会等待队列中删除等待节点并通过节点上的回调函数进行通知然后加入到cpu就绪队列中等待cpu调度执行.其具体流程主要包含以下两个处理逻辑,即休眠逻辑以及唤醒逻辑.
休眠逻辑
- linux 内核休眠逻辑核心代码
// 其中cmd = schedule(), 即一个调用schedule函数的指针
#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)
({
__label__ __out;
struct wait_queue_entry __wq_entry;
long __ret = ret; /* explicit shadow */
// 初始化过程(内部代码这里省略,直接说明)
// 1. 设置独占标志到当前节点entry
// 2. 将当前任务task指向节点的private
// 3. 同时为当前entry节点传递一个唤醒的回调函数autoremove_wake_function,一旦唤醒将会自动被删除
init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);
for (;;) {
// 防止队列中没有entry产生不断的轮询,主要处理wait_queue与entry节点添加或者删除
long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);
// 事件轮询检查是否事件有被唤醒
if (condition)
break;
if (___wait_is_interruptible(state) && __int) {
__ret = __int;
goto __out;
}
// 调用schedule()方法
cmd;
}
// 事件被唤醒,将当前的entry从队列中移除
finish_wait(&wq_head, &__wq_entry);
__out: __ret;
})
- 对此,我们可以总结如下:
- 在linux内核中某一个进程任务task执行需要等待某个条件condition被触发执行之前,首先会在内核中创建一个等待节点entry,然后初始化entry相关属性信息,其中将进程任务存放在entry节点并同时存储一个wake_callback函数并挂起当前进程
- 其次不断轮询检查当前进程任务task执行的condition是否满足,如果不满足则调用schedule()进入休眠状态
- 最后如果满足condition的话,就会将entry从队列中移除,也就是说这个时候事件已经被唤醒,进程处于就绪状态
唤醒逻辑
- linux内核的唤醒核心代码
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) {
unsigned flags = curr->flags;
int ret;
if (flags & WQ_FLAG_BOOKMARK)
continue;
//调用唤醒的函数
ret = curr->func(curr, mode, wake_flags, key);
if (ret < 0)
break;
// 检查当前节点是否为独占节点,如果是,即nr_exclusive = 0退出循环遍历
if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) &&
(&next->entry != &wq_head->head)) {
bookmark->flags = WQ_FLAG_BOOKMARK;
list_add_tail(&bookmark->entry, &next->entry);
break;
}
}
return nr_exclusive;
}
struct wait_queue_entry {
unsigned int flags;
void *private;
// 这里的func就是上述休眠的时候在init_wait_entry传递autoremove_wake_function
wait_queue_func_t func;
struct list_head entry;
};
// 唤醒函数
int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key)
{
// 公用的唤醒函数逻辑
// 内部执行try_to_wake_up, 也就是将wq_entry的private(当前进程)添加到cpu的执行队列中,让cpu能够调度task执行
int ret = default_wake_function(wq_entry, mode, sync, key);
// 其他为当前唤醒函数私有逻辑
if (ret)
list_del_init(&wq_entry->entry);
return ret;
}
EXPORT_SYMBOL(autoremove_wake_function);
- 对此,基于上述的唤醒逻辑可以总结如下:
- 在等待队列中循环遍历所有的entry节点,并执行回调函数,直到当前entry为排他节点的时候退出循环遍历
- 执行的回调函数中,存在私有逻辑与公用逻辑,类似模板方法设计模式
- 对于default_wake_function的唤醒回调函数主要是将entry的进程任务task添加到cpu就绪队列中等待cpu调度执行任务tas