【Linux驱动开发】Linux 设备驱动中的阻塞与非阻塞 I/O:机制、源码与示例

作者:赖small强日期:2025/11/20

Linux 设备驱动中的阻塞与非阻塞 I/O:机制、源码与示例

1. 基本概念与区别

  • 阻塞 I/O:当资源不可用时,调用方进入睡眠(TASK_INTERRUPTIBLE/TASK_UNINTERRUPTIBLE),直到条件满足或被信号打断后返回。
  • 非阻塞 I/O:当资源不可用时,系统调用立即返回(通常 -EAGAIN),不发生睡眠,由用户态自行决定重试或走多路复用。
  • 关键差异:
    • 进程状态:阻塞路径发生 TASK_RUNNING → TASK_* 的转换;非阻塞路径保持运行态。
    • 系统调用行为:阻塞路径等待条件成立;非阻塞路径迅速返回错误码。

2. 底层实现机制

等待队列 (wait_queue)

  • 类型与初始化:wait_queue_head_tinit_waitqueue_head()include/linux/wait.h:39-79)。
  • 睡眠与唤醒:wait_event*() 系列宏执行入队、设置任务睡眠态与调度;wake_up*() 负责唤醒等待者(include/linux/wait.h:165-175, 399-425)。
  • 内核实现:入队/出队与通用唤醒逻辑在 kernel/sched/wait.c:14-22, 65-77, 89-98, 171-183, 199-220

file_operations 中的相关方法

  • read/writeO_NONBLOCK:驱动读取 file->f_flags & O_NONBLOCK 决定是否走非阻塞路径(drivers/char/random.c:1456-1460drivers/char/rtc.c:370-377)。
  • poll/select/epoll:驱动 .poll 必须调用 poll_wait(file, &wait_queue, wait) 将等待队列登记到调用方,依据资源状态返回掩码(include/linux/poll.h:42-46)。

调度器与进程状态转换

  • 阻塞路径设置 TASK_INTERRUPTIBLE/UNINTERRUPTIBLE 并调度;唤醒后恢复运行。相关实现与内存序保证见 kernel/sched/wait.c:159-170, 171-183

3. 驱动开发实践

阻塞模式实现步骤

  1. 初始化等待队列头:init_waitqueue_head(&wq)(示例:drivers/char/example_blocking.c:103-105)。
  2. 检查资源并判断条件(示例:drivers/char/example_blocking.c:29-40)。
  3. 睡眠等待:wait_event_interruptible(wq, condition),被信号打断返回 -ERESTARTSYS(示例:drivers/char/example_blocking.c:38-40,参照 drivers/char/random.c:1448-1453)。
  4. 唤醒机制:资源状态变化后调用 wake_up_interruptible(&wq)(示例写路径:drivers/char/example_blocking.c:73-74)。

非阻塞模式实现要点

  • 检查 O_NONBLOCKfile->f_flags & O_NONBLOCKdrivers/char/example_blocking.c:35-37)。
  • 返回 -EAGAIN:资源不可用立即返回(drivers/char/random.c:1445-1447drivers/char/rtc.c:370-373)。
  • 即时检测逻辑:快速判断可读/可写并返回,避免睡眠和忙等。

.poll/select/epoll 实现

  • .poll 中调用 poll_wait(file, &wq, wait) 登记等待队列(drivers/char/example_blocking.c:80-81)。
  • 返回掩码:可读返回 POLLIN|POLLRDNORM,可写返回 POLLOUT|POLLWRNORM(示例:drivers/char/example_blocking.c:82-87;参考 drivers/char/random.c:1492-1496drivers/char/rtc.c:800-809)。

4. 性能与适用场景分析

  • 阻塞 I/O:
    • 优点:避免忙等,CPU 利用率高;适合低吞吐但需要正确同步的场景。
    • 缺点:响应受资源就绪时间影响,存在上下文切换。
  • 非阻塞 I/O:
    • 优点:适合实时与事件驱动;配合 poll/select/epoll 多路复用。
    • 缺点:用户态需编排重试与回退,不当实现可能忙等。
  • 混合策略:同时提供阻塞与非阻塞路径,并实现 .poll;用户空间以 select/poll/epoll 统一管理,参照 /dev/randomdrivers/char/random.c:1425-1460, 1484-1497, 1591-1599)。

5. 用户空间接口

  • 设置非阻塞:open(path, O_NONBLOCK)fcntl(fd, F_SETFL, O_NONBLOCK)
  • 行为表现:
    • 阻塞读:无数据时睡眠;收到信号返回 -ERESTARTSYSdrivers/char/random.c:1451-1453drivers/char/rtc.c:374-377)。
    • 非阻塞读:无数据立刻返回 -EAGAIN
    • poll/select/epoll:驱动 .poll 返回掩码,内核登记等待队列并在事件到来时唤醒调用方(include/linux/poll.h:42-46)。

6. 调试与问题排查

  • 常见死锁:持有不可睡眠锁(自旋锁)期间调用睡眠原语;应在睡眠前释放锁或使用合适锁策略。示例代码采用 mutex,在睡眠前释放(drivers/char/example_blocking.c:33-40)。
  • 竞态防护:在持锁下修改资源状态并确保等待/唤醒条件一致,之后再唤醒;等待宏内部已有必要的内存序(kernel/sched/wait.c:86-96)。
  • 核查要点:.poll 必须调用 poll_wait;错误码(-EAGAIN/-ERESTARTSYS)符合约定;唤醒路径触发预期事件掩码。

7. 源码对照与示例

/dev/random

  • 阻塞读与 O_NONBLOCKdrivers/char/random.c:1425-1460(非阻塞 -EAGAIN1445-1447;信号中断:1451-1453)。
  • .polldrivers/char/random.c:1484-1497
  • file_operationsdrivers/char/random.c:1591-1599

/dev/rtc

/dev/mem

  • 直接读写物理内存,无等待队列与睡眠:drivers/char/mem.c:102-173, 174-243

等待队列与 poll API

  • wait_event_interruptible 宏:include/linux/wait.h:399-425
  • wake_up_interruptible 宏:include/linux/wait.h:171-175
  • poll_waitinclude/linux/poll.h:42-46
  • 核心实现:kernel/sched/wait.c:14-22, 65-77, 89-98, 171-183, 199-220

8. 典型驱动示例(已集成)

  • 文件:drivers/char/example_blocking.c
  • 关键路径:
    • 阻塞/非阻塞读:drivers/char/example_blocking.c:22-51
    • 写入与唤醒:drivers/char/example_blocking.c:53-75
    • .polldrivers/char/example_blocking.c:77-88
    • 等待队列初始化与设备注册:drivers/char/example_blocking.c:103-123

9. 构建与启用

10. 用户态测试程序

  • 文件:samples/exblk/exblk_test.c
  • 编译:
    • cc -O2 -Wall -o exblk_test samples/exblk/exblk_test.c
  • 运行与验证:
    • 非阻塞读:程序启动即测试 O_NONBLOCK 路径(预期 EAGAIN)。
    • poll/epoll:在另一终端执行 echo "hello" > /dev/exblk 触发事件;程序读取并打印内容。
    • 阻塞读:提示后执行写入,可观察阻塞到事件到来。

【Linux驱动开发】Linux 设备驱动中的阻塞与非阻塞 I/O:机制、源码与示例》 是转载文章,点击查看原文


相关推荐


Vue + Axios + Node.js(Express)如何实现无感刷新Token?
郭晟玮2025/11/19

在前后端分离架构中,Vue 前端配合 Axios 发起请求,Node.js(Express)搭建后端服务时,可实现 Token 无感刷新以提升用户体验。具体而言,前端 Vue 项目通过 Axios 拦截器,在每次请求前检查 Token 状态。若 Token 即将过期,先向服务端发起静默刷新请求,Express 后端验证旧 Token 后颁发新 Token。前端拦截器收到新 Token 后,将其更新到本地存储,并重新发起原请求,整个过程对用户透明,无需手动重新登录。 页面基本流程 登录成功


如何使用 Spec Kit 工具进行规范驱动开发?
磊磊落落2025/11/18

大家好,我是磊磊落落,目前我在技术上主要关注:Java、Golang、AI、架构设计、云原生和自动化测试。欢迎来我的博客(leileiluoluo.com)获取我的最近更新! 由上文「Markdown 将成为 AI 时代的通用编程语言?」可以知道,规范驱动开发可能成为 AI 时代的开发新范式。 在传统软件开发流程中,规范只是编码前的临时脚手架,开发者一旦进入编码阶段,便将规范束之高阁。而进入 AI 时代,「规范驱动开发」想彻底改变这一现状,即让规范贯穿整个软件开发生命周期、让规范变得可执行、让


FPGA工程师12实战项目-基于PCIe的高速ADC采集项目
第二层皮-合肥2025/11/17

目录 简介 项目内容 项目内容 实战内容 最后做总结 简介 最近新凯莱的高速示波器项目很火爆,于是计划做一高速示波器的实战项目,由于硬件电路设计已经安排了,在同步安排一篇关于FPGA的。(计划教学5名学员) 项目内容 本方案基于XINLINX的K7系列FPGA,ADC选用AD9226。 项目内容 FPGA段固件程序:负责采集前端ADC的信号,FPGA基本框架,数据协议 PCIe卡驱动:负责上位机测试程序与PCie采集卡的数据交互 PC段测试程序:


零信任架构下的 WebAIGC 服务安全技术升级方向
LeonGao2025/11/16

前言:我们已不再“相信”一切 在互联网江湖的旧时代,安全防线的哲学像是一座古城门: “只要进了城,全是自己人;只要在外面,全是坏人。” 这种“城内无敌”的逻辑简单粗暴,但当我们的 应用、用户和AI模型 分散到全球各地的云端节点上时,城门的概念变得像《三体》的面壁计划——看似防御,实则透明。 于是,新时代的口号变成了: 零信任(Zero Trust)——默认无信任,一切验证重启。 一、零信任理念的内核哲学 如果把计算系统比作一个社会,那么“零信任”就像是一个反乌托邦的理性国度: 公民(


[Unity Shader Base] RayMarching in Cloud Rendering
一步一个foot-print2025/11/14

基础知识: 1.SDF 有符号距离场,且通过正负可以判断在物体外部还是内部,通常外正内负 这是RayMarching的灵魂支撑,能够通过一个数学函数,输入一个空间中的点,输出这个点到物体表面的最短距离(带符号)。可以使复杂的几何形状可以通过简单的 SDF运算来组合。比如,两个球体的 SDF 可以通过 min() 操作来融合,通过 max() 来相交,通过 abs()和减法来创造出“镂空”效果。 RayMarching 区别于正常的射线求交,根据他的中文翻译名,光线步进,可以比较生动


DNS正反向解析&转发服务器&主从服务
firstacui2025/11/13

DNS正反向解析&转发服务器&主从服务 1. 正反向解析 主机角色系统IPclient客户端redhat 9.6192.168.72.7server域名解析服务器redhat 9.6192.168.72.181.1 配置服务端 1)修改主机名和IP地址 [root@localhost ~]# hostnamectl hostname server [root@server ~]# nmcli c m ens160 ipv4.addresses 192.168.72.18/24 [root@s


CV论文速递:覆盖视频理解与生成、跨模态与定位、医学与生物视觉、图像数据集等方向(11.03-11.07)
CV实验室2025/11/11

本周精选12篇CV领域前沿论文,覆盖视频理解与生成、跨模态与定位、医学与生物视觉、图像数据集与模型优化等方向。全部200多篇论文感兴趣的自取! 原文 资料 这里! 一、视频理解与生成方向 1、Cambrian-S: Towards Spatial Supersensing in Video 作者:Shusheng Yang, Jihan Yang, Pinzhi Huang, Ellis Brown, Zihao Yang, Yue Yu, Shengbang Tong,


软考 系统架构设计师之考试感悟4
蓝天居士2025/11/10

接前一篇文章:软考 系统架构设计师之考试感悟3 昨天(2025年11月8日),本人第四次参加了软考系统架构师的考试。和前三次一样,考了一天,身心俱疲。这次感觉和上一次差不多,考的次数多了,也就习惯了。仍然有诸多感悟,下边将本次参加考试的感悟写在这里,以资自己及后来者借鉴。 上一次参加考试是今年的5月24号,地点还是前两次那个地方(本次也是) —— 北京市商业学院(远大路校区),坐公交只需要30分钟、骑车只需要15分钟左右。上次考试结果是在今年的6月26号、即考试后的一个月左右的时间出的。


C++:类和对象---进阶篇
仟千意2025/11/8

1. 类的默认成员函数 默认成员函数就是我们没有显式实现,C++会自动生成的成员函数称为默认成员函数,C++11后,C++类的默认成员函数有8个(默认构造函数、默认析构函数、拷贝构造函数、赋值运算符重载、取地址运算符重载、const取地址运算符重载、移动构造函数(C++11后)、移动赋值运算符重载(C++11后)),我们此文只了解重要的前4个,后4个中前两个不常用,后两个之后再做讲解。 2. 构造函数 构造函数是特殊的成员函数,虽名为构造,但它完成的是成员变量的初始化工作,所以它可以完美的


90%前端面试必问的12个JS核心,搞懂这些直接起飞!
良山有风来2025/11/5

你是不是也遇到过这样的场景?面试官抛出一个闭包问题,你支支吾吾答不上来;团队代码review时,看到同事用的Promise链一脸懵逼;明明功能实现了,性能却总是差那么一点... 别慌!今天我整理了12个JavaScript核心概念,这些都是2024年各大厂面试的高频考点,也是日常开发中真正实用的硬核知识。搞懂它们,不仅能轻松应对面试,更能让你的代码质量提升一个档次! 变量与作用域 先来看个最常见的面试题: // 经典面试题:猜猜输出什么? for (var i = 0; i < 3; i++)

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2025 聚合阅读