Linux驱动开发进阶:内核等待队列、轮询与异步通知详解
1. 等待队列1.1 概述等待队列(Wait Queue)是 Linux 内核中用于实现阻塞 I/O 的一种核心机制。当进程访问设备资源而资源暂时不可用时,内核可以让该进程进入睡眠状态,并将其挂入一个等待队列中;当资源变为可用时,内核再唤醒队列中的进程继续执行。这种“睡眠-唤醒”模式避免了进程忙等待(Busy-wait)带来的 CPU 浪费,是驱动开发中处理并发与资源竞争的基础手段。从使用场景来看,等待队列广泛用于字符设备驱动的读写操作、中断处理程序与进程之间的同步、以及内核线程的调度控制等场景。例如,当用户程序调用 read() 读取数据,而设备缓冲区为空时,驱动可以让调用进程进入睡眠,待数据到达后再唤醒它,从而显著提升系统整体效率。1.2. 实现原理等待队列的实现基于“链表 + 睡眠/唤醒”两个核心动作。其基本工作流程如下:初始化:驱动在初始化阶段创建并初始化一个等待队列头(wait_queue_head_t)。入队睡眠:当资源不可用时,进程调用 wait_event 系列宏,将当前进程以等待队列项(wait_queue_entry_t)的形式挂入队列,并进入睡眠状态。条件检查:进程睡眠前会先检查条件是否满足;若满足则直接返回,不进入睡眠。唤醒:当资源变为可用(通常在中断处理程序或另一进程上下文中),调用 wake_up 系列函数,遍历等待队列,将符合条件的进程唤醒。重新调度:被唤醒的进程重新获得 CPU 后,会再次检查条件;若条件仍不满足,则继续睡眠,否则返回调用者继续执行。这一机制的核心优势在于:进程在睡眠期间不占用 CPU,唤醒操作由事件驱动,从而实现了高效的资源等待与调度。1.3. 等待队列结构体等待队列涉及两个关键数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_entry_t)。下面分别说明其定义格式与参数含义。1.3.1 等待队列头等待队列头是整个等待队列的入口,定义在 include/linux/wait.h 中,其结构如下:struct wait_queue_head { spinlock_t lock; /* 保护等待队列的自旋锁 */ struct list_head head; /* 等待队列链表头 */ }; typedef struct wait_queue_head wait_queue_head_t;参数说明:lock:自旋锁,用于保护队列的并发访问,防止多个进程同时入队或出队时产生竞争。head:链表头,所有等待在该队列上的进程项都挂在此链表上。1.3.2 等待队列项在 Linux 内核的早期版本中,等待队列项使用wait_queue_t这一名称,它是wait_queue_entry_t的前身。其定义格式如下:typedef struct __wait_queue wait_queue_t; struct __wait_queue { unsigned int flags; /* 标志位,如 WQ_FLAG_EXCLUSIVE */ void *private; /* 私有数据,通常指向当前进程 task_struct */ wait_queue_func_t func; /* 唤醒回调函数 */ struct list_head task_list; /* 链表节点,用于挂入等待队列 */ };参数说明:flags:标志位,常用的有 WQ_FLAG_EXCLUSIVE,表示互斥等待,用于避免惊群效应。private:私有数据指针,通常指向当前进程的 task_struct 结构,用于唤醒时定位进程。func:唤醒回调函数,内核在唤醒进程时会调用该函数,默认实现为 default_wake_function。task_list:链表节点,用于将当前等待项挂入等待队列头的链表中。在较新的内核版本中,wait_queue_t被重命名为wait_queue_entry_t,二者在结构上保持一致,仅名称不同。驱动开发中建议使用新名称wait_queue_entry_t,以兼容较新的内核接口。等待队列项代表一个具体的等待进程,其定义如下:struct wait_queue_entry { unsigned int flags; /* 标志位,如 WQ_FLAG_EXCLUSIVE */ void *private; /* 私有数据,通常指向当前进程 task_struct */ wait_queue_func_t func; /* 唤醒回调函数 */ struct list_head entry; /* 链表节点,用于挂入等待队列 */ }; typedef struct wait_queue_entry wait_queue_entry_t;参数说明:flags:标志位,常用的有 WQ_FLAG_EXCLUSIVE,表示互斥等待,用于避免惊群效应(多个进程同时被唤醒但只有一个能获得资源)。private:私有数据指针,通常指向当前进程的 task_struct 结构,用于唤醒时定位进程。func:唤醒回调函数,内核在唤醒进程时会调用该函数,默认实现为 default_wake_function。entry:链表节点,用于将当前等待项挂入等待队列头的链表中。1.4 等待队列步骤在驱动开发中,将进程加入等待队列通常遵循以下步骤:定义等待队列头和等待实体:wait_queue_head_t在初始化阶段就要定义;当有进程需要等待事件时,定义一个wait_queue_t(或wait_queue_entry_t)等待实体,并调用init_waitqueue_entry()完成初始化。把等待实体加入到等待队列上:调用add_wait_queue()或add_wait_queue_exclusive()将等待实体挂入等待队列头的链表中。设置进程的状态为阻塞状态:通过set_current_state()将当前进程状态设置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。调用 schedule() 启动调度程序:让出 CPU,使进程进入睡眠状态,等待资源变为可用。设置进程的状态为 TASK_RUNNING:进程被唤醒后,调用set_current_state(TASK_RUNNING)恢复进程为可运行状态。注:从schedule()返回,并不代表外部事件产生,因为有可能因为信号中断而返回,所以从schedule()返回时,一般需要再测试条件。先从等待队列上移除:调用remove_wait_queue((p-r_wait), wait)将等待实体从等待队列中移除,再根据情况选择后续处理逻辑。其中,wait_queue_head_t和wait_queue_t的声明通常有两种方法:方法一:静态定义(编译期初始化)/* 静态定义等待队列头 */ static wait_queue_head_t my_wait_queue; init_waitqueue_head(my_wait_queue); /* 静态定义等待队列项 */ static wait_queue_t my_wait; init_waitqueue_entry(my_wait, current);方法二:动态定义(运行时初始化)/* 动态定义等待队列头 */ wait_queue_head_t *p_wait_queue; p_wait_queue = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); init_waitqueue_head(p_wait_queue); /* 动态定义等待队列项 */ wait_queue_t *p_wait; p_wait = kmalloc(sizeof(wait_queue_t), GFP_KERNEL); init_waitqueue_entry(p_wait, current);add_wait_queue() 函数add_wait_queue()用于将等待实体以非互斥方式挂入等待队列,其原型定义在 include/linux/wait.h 中:void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);参数说明:q:等待队列头指针,指向要挂入的等待队列。wait:等待实体指针,指向要加入队列的等待队列项。该函数将等待实体追加到队列链表尾部,并设置WQ_FLAG_EXCLUSIVE标志位为 0,表示非互斥等待。其实现核心代码如下:void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; wait-flags = ~WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(q-lock, flags); __add_wait_queue(q, wait); spin_unlock_irqrestore(q-lock, flags); }add_wait_queue_exclusive() 函数说明add_wait_queue_exclusive()用于将等待实体以互斥方式挂入等待队列,其原型如下:void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);参数说明:q:等待队列头指针,指向要挂入的等待队列。wait:等待实体指针,指向要加入队列的等待队列项。与add_wait_queue()不同,该函数会设置WQ_FLAG_EXCLUSIVE标志位为 1,表示互斥等待。互斥等待项在唤醒时只会唤醒队列中第一个符合条件的进程,从而有效避免惊群效应。其实现核心代码如下:void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; wait-flags |= WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(q-lock, fl

相关新闻

图像配准关键点手动标注:Python+OpenCV坐标导出脚本实战

图像配准关键点手动标注:Python+OpenCV坐标导出脚本实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 9:22:15 阅读更多 →
Apktool环境配置避坑指南:版本兼容与aapt2集成实战

Apktool环境配置避坑指南:版本兼容与aapt2集成实战

简介:本资源是一套面向Android逆向分析初学者与开发者的Mac平台APK资源提取工具集,聚焦解决APK反编译、资源解包与Smali代码查看等核心需求。压缩包共3个文件,包含可执行的apktool.jar(主程序)、适配Mac的shell启动脚本…

2026/10/16 9:22:15 阅读更多 →
C#与MCGS TCP通信实战:工业级握手、CRC校验与断线重连

C#与MCGS TCP通信实战:工业级握手、CRC校验与断线重连

简介:本资源是一套面向工业自动化开发者的C#与MCGS(昆仑通态)TCP通信实战范例,适用于具备基础C#编程能力及工控通信概念的工程师、PLC上位机开发初学者和高校自动化专业学生,解决上位机软件与MCGS嵌入式组态系统之间稳…

2026/10/16 9:22:15 阅读更多 →

最新新闻

Flutter × OpenHarmony 跨端勤工俭学App开发:数据结构与页面构建全解析

Flutter × OpenHarmony 跨端勤工俭学App开发:数据结构与页面构建全解析

如果你以为校园勤工俭学App就是把岗位列表和报名按钮拼在一起,那项目大概率会在第三周开始返工。我今年接手这个某高校勤工俭学平台改版时,需求列表已经从“能浏览岗位、能报名”一路膨胀到用工单位审批、面试排期、工时上报、月度结算,而且设…

2026/10/16 10:00:01 阅读更多 →
Matlab实现FDK算法:锥形束CT重建从原理到代码

Matlab实现FDK算法:锥形束CT重建从原理到代码

简介:这是一份面向医学CT成像与图像重建学习者的Matlab实现资源,围绕锥束CT(CBCT)的FDK算法展开,也包含MLEM、SART、SQS等迭代重建示例。代码按FDK重建流程组织:先对投影数据做加权(含Parker加权…

2026/10/16 10:00:01 阅读更多 →
Dify PDF解析实战:从文本抽取到RAG知识库的完整链路

Dify PDF解析实战:从文本抽取到RAG知识库的完整链路

做文档问答类项目,绕不开PDF。我手头这个项目叫【dify-pdf】,说白了就是在Dify平台上做一套PDF文档的处理与检索问答链路。最开始我以为PDF导入知识库是件小事,结果真正跑起来才发现,PDF这个格式从头到尾都在给RAG(检索…

2026/10/16 10:00:01 阅读更多 →
基于Spring Boot的濒危动物保护公益网站设计实战与答辩指南

基于Spring Boot的濒危动物保护公益网站设计实战与答辩指南

每到毕业季,我总能看到不少同学在选题和实现之间反复拉扯。选"基于Spring Boot的濒危动物保护公益网站"这个方向的人不算少,因为它是典型的"内容展示 业务闭环 公益属性"综合题:既有濒危动物科普内容需要展示&#xff…

2026/10/16 10:00:01 阅读更多 →
FDK重建算法实战:锥束CT投影几何、滤波反投影与伪影校正

FDK重建算法实战:锥束CT投影几何、滤波反投影与伪影校正

简介:这是一份基于Matlab的锥束CT(CBCT)三维重建算法实现,核心为FDK算法,适合医学影像、CT成像方向的学生与研究者快速理解和仿真。FDK重建严格按三步实现:先对投影数据加权,再进行滤波&#xf…

2026/10/16 10:00:01 阅读更多 →
数值计算误差分析三要素:舍入、截断与传播

数值计算误差分析三要素:舍入、截断与传播

1. 这份实验指导书到底在解决什么问题?“数值计算方法”这门课,很多同学一看到“误差分析”四个字就头皮发麻——不是因为概念多难懂,而是因为太容易被当成“纸上谈兵”。我带过几届本科生做上机实验,发现一个特别普遍的现象&…

2026/10/16 9:58:53 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →