正文
Linux 设备四种读写模型——其实核心就 4 种方式:查询、休眠 - 唤醒、poll、异步通知。它们不是中断本身,而是 “应用 - 驱动” 的上层交互逻辑(中断是底层硬件触发机制),但高效交互几乎都依赖中断实现。
核心原理:应用层主动、周期性查询设备状态,不管设备是否就绪,CPU 都在循环检查 —— 像你每隔 1 分钟去门口看快递到没到。
代码示例(应用层):
int main() {int fd = open("/dev/key", O_RDWR);if (fd < 0) { perror("open"); return -1; }int key_state;while (1) {read(fd, &key_state, sizeof(key_state)); // 主动查询if (key_state == 1) {printf("按键按下!n");break;}usleep(100000); // 100ms查一次,仍占CPU}close(fd);return 0;}
优点:实现最简单,无需驱动复杂逻辑,调试方便
缺点:CPU 占用率极高(哪怕设备几小时没响应),浪费资源
适用场景:仅调试或极简单设备(如 LED 状态查询),生产环境慎用!
核心原理:设备未就绪时,应用进程主动休眠(释放 CPU 给其他任务);当设备就绪(如按键按下触发中断),驱动唤醒进程继续执行 —— 像你听到门铃(中断)再去取快递,否则在家休息。
代码示例(驱动 + 应用):
驱动层(关键逻辑)static wait_queue_head_t key_waitq; // 等待队列static int key_pressed = 0; // 设备就绪标记// 读操作:未就绪则休眠static ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) {wait_event_interruptible(key_waitq, key_pressed); // 休眠copy_to_user(buf, &key_pressed, sizeof(key_pressed));key_pressed = 0;return sizeof(key_pressed);}// 中断服务函数:设备就绪时唤醒irqreturn_t key_isr(int irq, void *dev_id) {key_pressed = 1;wake_up_interruptible(&key_waitq); // 唤醒进程return IRQ_HANDLED;}应用层(极简)int main() {int fd = open("/dev/key", O_RDWR);int key_state;read(fd, &key_state, sizeof(key_state)); // 阻塞等待printf("按键按下!n");close(fd);return 0;}
优点:CPU 占用率极低(休眠时 0 占用),实现简单
缺点:不支持超时,只能阻塞等待单个设备
适用场景:大多数字符设备(按键、串口、传感器),嵌入式开发首选!
像你告诉快递员 “10 分钟内到就等,超时不等”,还能同时等快递和外卖。
驱动层(poll 函数实现)static unsigned int key_poll(struct file *filp, struct poll_table_struct *wait) {unsigned int mask = 0;poll_wait(filp, &key_waitq, wait); // 加入等待队列if (key_pressed) {mask |= POLLIN | POLLRDNORM; // 标记可读就绪}return mask;}// 驱动操作集绑定static const struct file_operations key_fops = {.owner = THIS_MODULE,.read = key_read,.poll = key_poll, // 关键:绑定poll函数};应用层(监控按键 + 超时)int main() {int fd = open("/dev/key", O_RDWR | O_NONBLOCK);struct pollfd fds[1] = {{fd, POLLIN, 0}}; // 关注可读事件while (1) {int ret = poll(fds, 1, 1000); // 等待1秒if (ret > 0 && (fds[0].revents & POLLIN)) {int key_state;read(fd, &key_state, sizeof(key_state));printf("按键按下!n");break;} else if (ret == 0) {printf("等待超时...n");}}close(fd);return 0;}
优点:CPU 占用率低,支持超时、支持多设备同时监控,兼容得阻塞 IO
缺点:实现复杂,高并发下开销大
适用场景:多设备监听、需超时(串口 + 网卡)
驱动层(异步通知实现)static struct fasync_struct *key_async;// 异步通知初始化static int key_fasync(int fd, struct file *filp, int mode) {return fasync_helper(fd, filp, mode, &key_async);}// 中断服务函数:发送信号irqreturn_t key_isr(int irq, void *dev_id) {if (key_async) {kill_fasync(&key_async, SIGIO, POLL_IN); // 发送SIGIO信号}return IRQ_HANDLED;}应用层(信号处理)void sigio_handler(int signum) { // 信号处理函数int fd = open("/dev/key", O_RDWR);int key_state;read(fd, &key_state, sizeof(key_state));printf("异步通知:按键按下!n");close(fd);}int main() {int fd = open("/dev/key", O_RDWR);fcntl(fd, F_SETOWN, getpid()); // 设置信号接收进程int flags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flags | FASYNC); // 启用异步通知signal(SIGIO, sigio_handler); // 注册信号处理函数// 主线程可自由执行其他逻辑while (1) {printf("主线程运行中...n");sleep(1);}close(fd);return 0;}
优点:CPU 占用率最低,完全异步,应用层无阻塞
缺点:实现复杂(需处理信号安全),信号可能丢失
适用场景:高实时性设备(网卡、磁盘 IO)、应用层需同时处理多任务的场景
本人专注 Linux 驱动 & Linux/Android BSP 开发调试,可接外包项目/技术支持/问题定位。有需求或交个朋友可加微信:【Chen_WeChat2025】。
全部0条评论
快来发表一下你的评论吧 !