news 2026/11/4 5:08:06

Linux 内核学习(19) --- linux waitqueue

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 内核学习(19) --- linux waitqueue

目录

    • Linux 等待队列
      • 背景和动机
      • 核心结构和概念
        • 什么是等待队列
        • 其余的任务阻塞方法
        • 等待队列和 select/poll/epoll 的关系
      • 进程状态和等待队列的交互
        • 使用等待队列时任务所处的状态
        • Linux 任务状态总览与进入原因
      • 主要 API 和数据结构
        • 数据结构
        • 等待队列和唤醒 API
        • 手工控制流程
      • 示例说明
        • 使用宏接口的典型方式
        • 手工控制的适用场景
      • 总体示意

Linux 等待队列

背景和动机

操作系统内核中,进程经常因为某种资源或者事件(设备I/O完成,数据到达,锁释放) 而无法继续执行,如果采用忙等的设计(busy-looping) 方式,不仅浪费CPU时间片,还是导致系统整体的吞吐量下降,更合理的做法时让进程主动放弃CPU进入休眠状态,待等待条件满足时再由内核唤醒
Linux内核为此提供了等待队列机制(wait queue),用于管理所有特定时间的等待的进程

等待队列在驱动中非常常见,例如:当用户进程通过read()系统调用从设备读取数据,而设备尚未准备好数据,驱动程序可以将此进程挂入等待队列并调度出去,待中断处理程序接收到数据后,再唤醒对列中的进程继续执行,这种机制支撑了 阻塞IO、select/poll多路复用,异步信号通知等上层语义的
实现

核心结构和概念

什么是等待队列

等待队列本质上一个由等待队列头(wait_queue_head_t) 管理的双向链表,链表中的每个节点(wait_queue_entry_t或者传统命名wait_queue_t)代表一个正在等待某件事情发生的任务
节点中封装了指向当前任务的task_struct的指针和一个唤醒回调的函数;当唤醒条件成立的时候,内核遍历链表,调用各个节点的回调将对应的任务设置为可运行状态
等待队列头通常嵌入在代表具体资源的对象中(比如设备结构体,socket、文件对象),使得谁使用这个自然资源一目了然

其余的任务阻塞方法

Linux任务进入阻塞态并非只能经过等待队列,完成量(completion) 内部包含等待队列头和完成标志
信号量(semaphore) 也借助等待队列管理睡眠者,互斥锁(mutex) 的慢路径也将当前的任务挂入自身的等待队列,换言之,多数高层同步原语在底层复用了等待队列的睡眠-唤醒骨架

还存在不经过等待队列的阻塞状态:例如:TASK_UNINTERRUPTIABLE状态可因vfork等待子进程exec/exit而进入,调度器直接处理,不依赖外部时间队列的现式入队操作。

因此更加准确的表述是:
等待队列是Linux实现可唤醒阻塞的主要且最通用的基础设施,不是唯一手段

等待队列和 select/poll/epoll 的关系

select/poll/epoll的阻塞能力建立在等待队列之上,但是等待队列并非为它们而存在
三者的核心流程都包括:向被监视的文件f_op->poll注册一个回调节点到该文件对应的等待队列头,然后调用schedule()睡眠,当文件状态变化的时候,驱动或者协议栈唤醒该等待队列,回调就将就绪时间通知到复用层,差异在于:select/poll每次调用都重新遍历并注册节点,而epoll通过红黑树与就绪链表在首次注册之后持续跟踪,等待队列回调仅仅将epitem移入就绪链表,避免重复遍历
因此,等待队列是它们实现阻塞等待的共用底层机制,但是epoll在此基础上做了显著优化

进程状态和等待队列的交互

使用等待队列时任务所处的状态

调用wait_event_interruptiable()系列宏的时候,任务在确定条件不成立后,被设置为TASK_INTERRUPTIABLE并调用schedule让出CPU,如果使用wait_event则设置为TASK_INTERRUPTIABLE
二者的区别在于睡眠期间是否响应信号:前者可被信号唤醒并返回-ERESTARTSYS,后者不受信号干扰。通常可用于不可中断的硬件交互或者关键的内核路径,唤醒时,wake_up系列函数遍历等待队列,通过回调将任务改回TASK_RUNNING并放入运行队列

Linux 任务状态总览与进入原因
状态含义典型进入原因
TASK_RUNNING可运行(包含正在运行和就绪)被唤醒、时间片轮转、新建任务
TASK_INTERRUPTIABLE可中断睡眠等待 I/O、等待信号量,wait_event_interuuptibale等可被信号打断的等待
TASK_UNINTERRUPTIABLE不可中断睡眠等待硬件完成、关键内核路径中的短时等待,wait_event系列
TASK_STOPPED停止收到SIGSTOP、SIGSTP等停止信号
TASK_TRACED被跟踪被ptrace调试器控制
TASK_DEAD/TASK_ZOBIE僵尸/终止调用do_exit()后等待父进程回收

等待队列直接涉及的是TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE,唤醒操作将任务从二者切回TASK_RUNNING

主要 API 和数据结构

数据结构
名称作用
wait_queue_head_t等待队列头,内部包含自旋锁与链表头,代表一个可等待的事件源
wait_queue_entry_t等待队列节点,包含task_struct指针、唤醒函数fun和标志位

静态定义等待队列头使用DRCLARE_WAIT_QURUE_HEAD(name),
动态初始化使用init_waitqueue_head(&head)

等待队列和唤醒 API
API语义
wait_event(wq, condition)不可中断等待,直到 condition 为真
wait_event_interruptible(wq, condition)可中断等待,信号到达时返回 -ERESTARTSYS
wait_event_timeout(wq, conditon, timeout)带超时的不可中断等待
wait_event_interruptible_timeout(wq, conditon, timeout)带超时的可中断等待
wake_up(&wq)唤醒队列上所有的非独占等待者(含 TASK_UNINTERRUPTIBLE)
wake_up_interruptible(&wq)仅仅唤醒 TASK_INTERRUPTIBLE 等待者
wake_up_all/wake_up_interruptible_all忽略独占标志,唤醒全部等待者

wake_up 和 wake_up_interruptible 均会处理独占节点(WQ_FLAG_EXCLUSIVE),对于独占节点只唤醒一个,避免惊群效应(独占节点如何处理)

手工控制流程

宏的封装隐藏了细节,但是驱动开发中有时需要手工控制,核心步骤是:

  1. 入队并设置状态:prepare_to_wait(&wq, &entry, TASK_INTERRUPTIBLE)将当前的任务封装为节点加入队列,并设置进程状态

  2. 检查条件并调度: 在循环中检查条件,不满足则调用schedule让出cpu

  3. 清理:条件满足或者被唤醒后,finish_wait(&wq, &entry)将节点移出队列并恢复状态

示例说明

使用宏接口的典型方式

假设一个字符设备驱动需要让读操作阻塞直到缓冲区非空,驱动结构体嵌入wait_queue_head_t rq,读函数中:

if(wait_queue_interruptible(dev->rq,dev->count>0))return-ERESTARTSYS;

当写操作向缓冲区放入数据后,执行:

wake_up_interruptible(&dev->rq);

此模式简洁且不容易出错,适合多数场景,需要注意 condition 的求值要在宏内部完成,且每次唤醒后重新检查,确保即使被误唤醒也不会返回错误结果

手工控制的适用场景

当需要在睡眠前执行额外操作(释放锁,切换状态),或需要精细控制超时信号和信号处理的时候,手工流程更加灵活:

DEFINE_WAIT(entry);prepare_to_wait(&dev->rq,&entry,TASK_INTERRUPTIBLE);if(dev->count==0)schedule();finish_wait(&dev->rq,&entry);

总体示意


Linux内核中的Waitqueue(等待队列) 是一种任务等待和唤醒机制,允许多个任务在同一队列上等待特定条件,
当等待条件不满足时,任务将等待节点注册到队列中,并根据等待方式进入TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE等睡眠状态,通过schedule()让出 CPU
当其他执行上下文改变等待条件并调用wake_up()等接口时,内核遍历等待队列,调用相应等待节点的唤醒回调。对于普通任务,默认唤醒路径通过调度器的唤醒机制,使匹配的睡眠任务转为TASK_RUNNING,进入可运行状态
被唤醒的任务不会立即执行,而是等待调度器分配CPU。任务恢复执行后重新检查等待条件,若条件仍不满足,则可能再次进入睡眠;若条件满足,则退出等待过程,继续执行后续逻辑

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 2:41:02

2026年仍然支持 Windows7的浏览器

Windows 7在2020年就已结束官方支持,但国内仍有相当规模的设备在运行这个系统。浏览器作为连接互联网的核心入口,它的选择直接决定了这些老机器还能不能相对完整地打开今天的网页。主流通道早已关闭,但仍有几条路可以走,每一条路的…

作者头像 李华
网站建设 2026/11/4 5:07:45

“让普通人也能变成科学家”:端砚平台刷屏,这个口号我只信一半

“让普通人也能变成科学家”:端砚平台刷屏,这个口号我只信一半 这两天科技圈最出圈的一句话,是“让普通人也能变成科学家”。 说这话的,是上海人工智能实验室在 WAIC 2026 上发布的“书生端砚”科学发现平台。名字起得很有文化&am…

作者头像 李华