当前位置: 首页 > news >正文

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

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

  • .read 阻塞与非阻塞:drivers/char/rtc.c:329-394
  • .polldrivers/char/rtc.c:793-809

/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. 构建与启用

  • 配置项:CONFIG_EXAMPLE_BLOCKINGdrivers/char/Kconfig)。
  • Makefile 目标:drivers/char/Makefile:63
  • 设备节点:启用 devtmpfs/udev 自动生成 /dev/exblk;或手动创建:
    • mknod /dev/exblk c $(grep exblk /proc/devices | awk '{print $1}') 0

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 触发事件;程序读取并打印内容。
    • 阻塞读:提示后执行写入,可观察阻塞到事件到来。
http://www.dtcms.com/a/618226.html

相关文章:

  • HarmonyOS新闻卡片组件开发实战:自定义组件与List渲染深度解析
  • 解决:jenkins Exception java.lang.NoSuchFieldError: SNAKE_CASE
  • 如何实现Redis安装与使用的详细教程
  • tensorflow+yolo图片训练和图片识别系统
  • 唯品会 一家专门做特卖的网站现在前端开发用什么技术
  • 图神经网络分享系列-GraphSage(Inductive Representation Learning on Large Graphs) (一)
  • leetcode对称二叉树
  • 网站开发设计心得及体会河南建设工程造价管理协会网站
  • 深度学习实战:(2)用 TensorFlow 1.x 构建手语识别模型
  • 人工智能、机器学习、深度学习:技术革命的深度解析
  • 东营seo网站建设费用广告设计专业自我介绍
  • 【Linux】进程状态、进程优先级、进程切换和调度
  • 【Android】View 的工作原理
  • 行人跌倒智能检测系统:YOLOv8/V5/V6/V7 多模型 + PySide6 界面 深度学习 多场景适配 大数据 (建议收藏)✅
  • 山东网络推广图片福州seo网站管理
  • C#中Task的详细用法
  • 自己怎么做企业网站建设免费代理服务器ip地址
  • 前端 css selector 的层叠 优先级与继承
  • 基于python二手房数据分析系统 可视化 Scrapy 爬虫 链家二手房数据 Django框架 基于用户的协同过滤推荐 二手房推荐系统 (源码)✅
  • Rust 内部可变性的访问器模式
  • ThinkPHP8学习篇(十二):模型关联(二)
  • 药品行业做网站windows wordpress
  • 【读代码】LightRAG轻量级知识图谱增强检索系统的架构与实现
  • arm架构设备使用FISCO BCOS上搭建多机区块链网络
  • 【Android】LRU 与 Android 缓存策略
  • 使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 26--数据驱动--参数化处理 Excel 文件 3
  • 第41节:第三阶段总结:打造一个AR家具摆放应用
  • 建设网站流程2022年最新新闻播报稿件
  • 网站地图的作用长沙网站开发设计
  • 【读代码】最新端侧TTS模型NeuTTS-Air