正文
Linux 设备四种读写模型——其实核心就 4 种方式:查询、休眠 - 唤醒、poll、异步通知。它们不是中断本身,而是 “应用 - 驱动” 的上层交互逻辑(中断是底层硬件触发机制),但高效交互几乎都依赖中断实现。
1 查询方式
核心原理:应用层主动、周期性查询设备状态,不管设备是否就绪,CPU 都在循环检查 —— 像你每隔 1 分钟去门口看快递到没到。
代码示例(应用层):
intmain(){intfd =open("/dev/key", O_RDWR);if(fd < 0) {perror("open");return-1; }intkey_state;while(1) {read(fd, &key_state,sizeof(key_state));// 主动查询if(key_state ==1) {printf("按键按下!n");break;}usleep(100000);// 100ms查一次,仍占CPU}close(fd);return0;}
优缺点与场景
优点:实现最简单,无需驱动复杂逻辑,调试方便
缺点:CPU 占用率极高(哪怕设备几小时没响应),浪费资源
适用场景:仅调试或极简单设备(如 LED 状态查询),生产环境慎用!
2 休眠 - 唤醒
核心原理:设备未就绪时,应用进程主动休眠(释放 CPU 给其他任务);当设备就绪(如按键按下触发中断),驱动唤醒进程继续执行 —— 像你听到门铃(中断)再去取快递,否则在家休息。
代码示例(驱动 + 应用):
驱动层(关键逻辑)staticwait_queue_head_tkey_waitq;// 等待队列staticintkey_pressed =0;// 设备就绪标记// 读操作:未就绪则休眠staticssize_tkey_read(structfile *filp,char__user *buf,size_tcount,loff_t*ppos){wait_event_interruptible(key_waitq, key_pressed);// 休眠copy_to_user(buf, &key_pressed,sizeof(key_pressed));key_pressed =0;returnsizeof(key_pressed);}// 中断服务函数:设备就绪时唤醒irqreturn_tkey_isr(intirq,void*dev_id){key_pressed =1;wake_up_interruptible(&key_waitq);// 唤醒进程returnIRQ_HANDLED;}应用层(极简)intmain(){intfd =open("/dev/key", O_RDWR);intkey_state;read(fd, &key_state,sizeof(key_state));// 阻塞等待printf("按键按下!n");close(fd);return0;}
优缺点与场景
优点:CPU 占用率极低(休眠时 0 占用),实现简单
缺点:不支持超时,只能阻塞等待单个设备
适用场景:大多数字符设备(按键、串口、传感器),嵌入式开发首选!
3 poll 方式
核心原理:结合 “查询” 和 “休眠 - 唤醒” 的优点:应用层通过poll()/select()设置超时时间,内核监控多个设备 —— 超时前设备就绪则立即返回,超时后也会唤醒,避免无限阻塞。
像你告诉快递员 “10 分钟内到就等,超时不等”,还能同时等快递和外卖。
代码示例(驱动 + 应用):
驱动层(poll 函数实现)staticunsignedintkey_poll(structfile *filp,structpoll_table_struct *wait){unsignedintmask =0;poll_wait(filp, &key_waitq, wait);// 加入等待队列if(key_pressed) {mask |= POLLIN | POLLRDNORM;// 标记可读就绪}returnmask;}// 驱动操作集绑定staticconststructfile_operationskey_fops = {.owner = THIS_MODULE,.read = key_read,.poll = key_poll,// 关键:绑定poll函数};应用层(监控按键 + 超时)intmain(){intfd =open("/dev/key", O_RDWR | O_NONBLOCK);structpollfdfds[1] = {{fd, POLLIN,0}};// 关注可读事件while(1) {intret =poll(fds,1,1000);// 等待1秒if(ret >0&& (fds[0].revents & POLLIN)) {intkey_state;read(fd, &key_state,sizeof(key_state));printf("按键按下!n");break;}elseif(ret ==0) {printf("等待超时...n");}}close(fd);return0;}
优缺点与场景
优点:CPU 占用率低,支持超时、支持多设备同时监控,兼容得阻塞 IO
缺点:实现复杂,高并发下开销大
适用场景:多设备监听、需超时(串口 + 网卡)
4 异步通知
核心原理:完全反转交互方向:应用层无需查询 / 等待,设备就绪时(触发中断),驱动主动发送信号(如 SIGIO)通知应用层,应用层执行信号处理函数 —— 像快递员直接把快递送上门,不用你等。
代码示例(驱动 + 应用)
驱动层(异步通知实现)staticstructfasync_struct*key_async;// 异步通知初始化staticintkey_fasync(intfd,structfile *filp,intmode){returnfasync_helper(fd, filp, mode, &key_async);}// 中断服务函数:发送信号irqreturn_tkey_isr(intirq,void*dev_id){if(key_async) {kill_fasync(&key_async, SIGIO, POLL_IN);// 发送SIGIO信号}returnIRQ_HANDLED;}应用层(信号处理)voidsigio_handler(intsignum){// 信号处理函数intfd =open("/dev/key", O_RDWR);intkey_state;read(fd, &key_state,sizeof(key_state));printf("异步通知:按键按下!n");close(fd);}intmain(){intfd =open("/dev/key", O_RDWR);fcntl(fd, F_SETOWN,getpid());// 设置信号接收进程intflags =fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flags | FASYNC);// 启用异步通知signal(SIGIO, sigio_handler);// 注册信号处理函数// 主线程可自由执行其他逻辑while(1) {printf("主线程运行中...n");sleep(1);}close(fd);return0;}
优缺点与场景
优点:CPU 占用率最低,完全异步,应用层无阻塞
缺点:实现复杂(需处理信号安全),信号可能丢失
适用场景:高实时性设备(网卡、磁盘 IO)、应用层需同时处理多任务的场景
本人专注 Linux 驱动 & Linux/Android BSP 开发调试,可接外包项目/技术支持/问题定位。有需求或交个朋友可加微信:【Chen_WeChat2025】。
-
Linux
+关注
关注
88文章
11825浏览量
219617
发布评论请先 登录
如何理解Linux内核中的PCIe驱动
驱动之路#20:Pinctrl 在手,引脚复用很顺手
变频器四种制动方式
驱动之路#04:LCD 驱动程序分析(基于RK3576)
低成本CAN扩展方案怎么选?CSM331A四种模式一次说清
是德示波器DSOX1202A与电脑的四种连接方式及操作步骤详解
【免费送书】成为硬核Linux开发者:《Linux 设备驱动开发(第 2 版)》
【书籍评测活动NO.67】成为硬核Linux开发者:《Linux 设备驱动开发(第 2 版)》
一图看懂绿电直连的四种玩法
从入门到精通:基于开源代码的BLE四种模式开发详解
全网最全CSA3412,BCT4340,VL162,MCU/ USB3.1 正反插10G bps四种解决方案
驱动之路#24:Linux设备四种读写模型
评论