linux驱动中的非阻塞机制

本文深入探讨了非阻塞I/O机制中的select和poll函数,包括它们的内部实现细节和使用方法。通过分析文件描述符集合、超时设置及与设备驱动交互的过程,为读者提供了全面的理解。

在 Linux 设备驱动中,​非阻塞机制允许用户程序在设备未就绪时立即返回,而不是等待资源可用。这种机制通过 ​O_NONBLOCK 标志和 ​轮询(poll/select/epoll)​ 实现,适用于需要高响应性或异步 I/O 的场景。以下是详细的实现方法和代码示例:


1. 用户空间启用非阻塞模式

用户程序在打开设备时指定 O_NONBLOCK 标志:

int fd = open("/dev/mydevice", O_RDWR | O_NONBLOCK);

2. 驱动中的非阻塞处理

​(1) 检查 O_NONBLOCK 标志

在驱动实现的 read/write 等方法中,通过 filp->f_flags 判断是否启用非阻塞模式:

ssize_t mydev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) {
    struct mydev_private *priv = filp->private_data;

    // 非阻塞模式下,无数据时直接返回 -EAGAIN
    if ((filp->f_flags & O_NONBLOCK) && !priv->data_ready) {
        return -EAGAIN;
    }

    // 阻塞模式下等待数据就绪
    wait_event_interruptible(priv->wait_queue, priv->data_ready);
    // ...数据拷贝操作...
}
​(2) 立即返回未就绪状态

当设备无法立即完成操作时,返回 -EAGAIN

ssize_t mydev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos) {
    if (device_busy) {
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;  // 非阻塞模式直接返回
        else
            wait_event(...);  // 阻塞模式等待
    }
    // ...写入操作...
}

3. 实现 Poll 方法支持轮询

为了让用户程序通过 select/poll/epoll 检测设备状态,需实现 file_operations.poll

unsigned int mydev_poll(struct file *filp, struct poll_table_struct *wait) {
    struct mydev_private *priv = filp->private_data;
    unsigned int mask = 0;

    // 将等待队列添加到 poll_table
    poll_wait(filp, &priv->wait_queue, wait);

    // 检查设备状态
    if (priv->data_ready)
        mask |= POLLIN | POLLRDNORM;  // 数据可读
    if (buffer_space_available)
        mask |= POLLOUT | POLLWRNORM; // 数据可写

    return mask;
}

// 注册到 file_operations
static struct file_operations mydev_fops = {
    .owner = THIS_MODULE,
    .read = mydev_read,
    .write = mydev_write,
    .poll = mydev_poll,
};

4. 用户空间轮询示例

用户程序使用 poll 监控设备状态:

#include <poll.h>

struct pollfd fds[1];
fds[0].fd = fd;          // 设备文件描述符
fds[0].events = POLLIN;  // 关注可读事件

int ret = poll(fds, 1, 1000);  // 超时 1000ms
if (ret > 0) {
    if (fds[0].revents & POLLIN) {
        // 数据可读,执行 read 操作
        read(fd, buf, sizeof(buf));
    }
}

5. 关键机制详解

​(1) 非阻塞模式下的行为
  • read/write:若资源未就绪,立即返回 -EAGAIN
  • poll/select:返回当前设备状态(如 POLLIN 表示可读),无需阻塞。
​(2) 等待队列管理
  • 数据就绪时唤醒队列:在中断处理或任务中触发唤醒:
    wake_up_interruptible(&priv->wait_queue);
​(3) 并发与竞态控制
  • 自旋锁保护共享数据
    spin_lock(&priv->lock);
    priv->data_ready = 1;
    spin_unlock(&priv->lock);
    wake_up_interruptible(&priv->wait_queue);

6. 完整驱动示例

#include <linux/module.h>
#include <linux/poll.h>
#include <linux/wait.h>

static DECLARE_WAIT_QUEUE_HEAD(wait_queue);
static int data_ready = 0;

ssize_t mydev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) {
    if (filp->f_flags & O_NONBLOCK && !data_ready)
        return -EAGAIN;

    wait_event_interruptible(wait_queue, data_ready);
    data_ready = 0;
    return simple_read_from_buffer(buf, count, pos, "data", 5);
}

unsigned int mydev_poll(struct file *filp, struct poll_table_struct *wait) {
    poll_wait(filp, &wait_queue, wait);
    unsigned int mask = 0;
    if (data_ready)
        mask |= POLLIN;
    return mask;
}

static struct file_operations mydev_fops = {
    .owner = THIS_MODULE,
    .read = mydev_read,
    .poll = mydev_poll,
};

// 模拟数据到达(例如在中断中调用)
void simulate_data_ready(void) {
    data_ready = 1;
    wake_up_interruptible(&wait_queue);
}

7. 测试与调试

​(1) 用户空间测试程序
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    int fd = open("/dev/mydevice", O_RDONLY | O_NONBLOCK);
    char buf[10];
    ssize_t ret = read(fd, buf, sizeof(buf));
    if (ret == -1) {
        perror("read failed"); // 预期输出:"read failed: Resource temporarily unavailable"
    }
    close(fd);
    return 0;
}
​(2) 查看等待队列状态

通过 /proc/<pid>/wchan 查看进程等待的队列:

cat /proc/$(pidof my_app)/wchan

总结

  • 非阻塞模式:通过 O_NONBLOCK 标志启用,驱动返回 -EAGAIN 避免阻塞。
  • 轮询支持:实现 poll 方法,返回设备就绪状态。
  • 性能优化:减少进程休眠,提升响应速度。
  • 适用场景:实时系统、高性能服务器、GUI 应用等需快速响应的场景。

通过合理实现非阻塞机制,可显著提升设备驱动的灵活性和系统整体性能。

参考:

  1. linux系统下的非阻塞访问和阻塞访问_linux 阻塞 非阻塞 区别-优快云博客 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浩瀚之水_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值