当前位置:首页>Linux>驱动之路#24:Linux 设备4种读写模型

驱动之路#24:Linux 设备4种读写模型

  • 2026-10-11 08:07:39
驱动之路#24:Linux 设备4种读写模型

题图:2015年,一位英国艺术家提出,叠石头也是一种艺术,应该举办比赛。他从2016年开始,每年都在苏格兰的一处海滩上,举办欧洲叠石头锦标赛,上图就是2019年的冠军作品。

欢迎关注,每周更新!☞


本合集分享的是,我当初学习Linux驱动的来时路——《《驱动之路》开篇:自序&前言》。

正文

Linux 设备四种读写模型——其实核心就 4 种方式:查询、休眠 - 唤醒、poll、异步通知。它们不是中断本身,而是 “应用 - 驱动” 的上层交互逻辑(中断是底层硬件触发机制),但高效交互几乎都依赖中断实现。

1 查询方式

核心原理:应用层主动、周期性查询设备状态,不管设备是否就绪,CPU 都在循环检查 —— 像你每隔 1 分钟去门口看快递到没到。

代码示例(应用层):

#include<stdio.h>#include<fcntl.h>#include<unistd.h>intmain(){    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 状态查询),生产环境慎用!

2 休眠 - 唤醒

核心原理:设备未就绪时,应用进程主动休眠(释放 CPU 给其他任务);当设备就绪(如按键按下触发中断),驱动唤醒进程继续执行 —— 像你听到门铃(中断)再去取快递,否则在家休息。

代码示例(驱动 + 应用):

驱动层(关键逻辑)#include<linux/wait.h>#include<linux/sched.h>static wait_queue_head_t key_waitq; // 等待队列static int key_pressed = 0; // 设备就绪标记// 读操作:未就绪则休眠staticssize_tkey_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_tkey_isr(int irq, void *dev_id){    key_pressed = 1;    wake_up_interruptible(&key_waitq); // 唤醒进程    return IRQ_HANDLED;}应用层(极简)intmain(){    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 占用),实现简单

❌缺点:不支持超时,只能阻塞等待单个设备

📌适用场景:大多数字符设备(按键、串口、传感器),嵌入式开发首选!

3 poll 方式

核心原理:结合 “查询” 和 “休眠 - 唤醒” 的优点:应用层通过poll()/select()设置超时时间,内核监控多个设备 —— 超时前设备就绪则立即返回,超时后也会唤醒,避免无限阻塞。

像你告诉快递员 “10 分钟内到就等,超时不等”,还能同时等快递和外卖。

代码示例(驱动 + 应用):

驱动层(poll 函数实现)#include<linux/poll.h>staticunsignedintkey_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函数};应用层(监控按键 + 超时)#include<poll.h>intmain(){    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

❌ 缺点:实现复杂,高并发下开销大

📌适用场景:多设备监听、需超时(串口 + 网卡)

4 异步通知

核心原理:完全反转交互方向:应用层无需查询 / 等待,设备就绪时(触发中断),驱动主动发送信号(如 SIGIO) 通知应用层,应用层执行信号处理函数 —— 像快递员直接把快递送上门,不用你等。

代码示例(驱动 + 应用)

驱动层(异步通知实现)#include<linux/fasync.h>static struct fasync_struct *key_async;// 异步通知初始化staticintkey_fasync(int fd, struct file *filp, int mode){    return fasync_helper(fd, filp, mode, &key_async);}// 中断服务函数:发送信号irqreturn_tkey_isr(int irq, void *dev_id){    if (key_async) {        kill_fasync(&key_async, SIGIO, POLL_IN); // 发送SIGIO信号    }    return IRQ_HANDLED;}应用层(信号处理)#include<signal.h>voidsigio_handler(int signum){ // 信号处理函数    int fd = open("/dev/key", O_RDWR);    int key_state;    read(fd, &key_state, sizeof(key_state));    printf("异步通知:按键按下!\n");    close(fd);}intmain(){    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】。

更多原创技术文章:《README 2026》。

(完)

最新文章

随机文章