找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

428

积分

0

好友

62

主题
发表于 5 天前 | 查看: 15| 回复: 0

在支持轮询操作的 globalfifo 驱动中,核心在于实现 struct file_operations 中的 .poll 方法。

首先,需要在设备数据结构中定义等待队列头,用于管理等待可读或可写事件的进程。

#define GLOBALFIFO_SIZE 4096
struct globalfifo_dev {
    // ...
    unsigned int current_len;      // 当前数据长度
    struct mutex lock;             // 并发控制锁
    wait_queue_head_t read_wait;   // 可读等待队列
    wait_queue_head_t write_wait;  // 可写等待队列
};

globalfifo_poll 函数的实现需要完成两个核心任务:注册等待队列和返回设备当前状态掩码。

static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
    struct globalfifo_dev *dev = filp->private_data;
    unsigned int mask = 0;

    mutex_lock(&dev->lock);

    // 1. 注册等待:将当前进程注册到 read/write 等待队列上
    poll_wait(filp, &dev->read_wait, wait);
    poll_wait(filp, &dev->write_wait, wait);

    // 2. 检查设备状态并设置掩码
    if (dev->current_len > 0) {
        // FIFO中有数据,可读
        mask |= POLLIN | POLLRDNORM;
    }
    if (dev->current_len < GLOBALFIFO_SIZE) {
        // FIFO有空间,可写
        mask |= POLLOUT | POLLWRNORM;
    }

    mutex_unlock(&dev->lock);
    return mask;
}

接下来,需要将实现的 poll 方法集成到驱动的文件操作表中。

static const struct file_operations globalfifo_fops = {
    // ...
    .read    = globalfifo_read,
    .write   = globalfifo_write,
    .poll    = globalfifo_poll, // 注册 poll 方法
    // ...
};

最后,在用户空间可以通过 poll() 系统调用来验证驱动是否工作正常。以下是一个示例程序,它监控 /dev/globalfifo 设备的可读事件。当设备 FIFO 为空时,poll() 会阻塞;当在另一个终端向设备写入数据后,等待的进程会被唤醒,poll() 返回并指示设备可读。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>      // 轮询相关的头文件
#include <errno.h>

#define FIFO_DEVICE "/dev/globalfifo"
#define BUF_SIZE 1024
#define TIMEOUT_MS 5000 // 5秒超时

int main(void)
{
    int fd;
    int ret;
    struct pollfd fds[1];
    char buf[BUF_SIZE];

    // 1. 打开设备
    fd = open(FIFO_DEVICE, O_RDWR | O_NONBLOCK); // 建议使用非阻塞模式配合 poll
    if (fd < 0) {
        perror(“Failed to open FIFO_DEVICE”);
        return 1;
    }

    // 2. 设置 pollfd 结构体
    fds[0].fd = fd;
    fds[0].events = POLLIN; // 我们关注可读事件 (POLLIN)

    printf(“Monitoring %s for data (Timeout: %dms).\n”, FIFO_DEVICE, TIMEOUT_MS);
    printf(“Please open another terminal and write data to the device (e.g., ‘echo hello > %s’).\n”, FIFO_DEVICE);

    // 3. 调用 poll 系统调用
    ret = poll(fds, 1, TIMEOUT_MS);
    if (ret < 0) {
        perror(“Poll error”);
        close(fd);
        return 1;
    } else if (ret == 0) {
        // ret == 0 表示超时
        printf(“Poll timeout (%dms) reached. No data arrived.\n”, TIMEOUT_MS);
    } else {
        // ret > 0 表示有文件描述符就绪
        printf(“\nDevice state changed! Poll returned %d event(s).\n”, ret);

        // 4. 检查具体事件
        if (fds[0].revents & POLLIN) {
            printf(“>>> %s is ready for reading (POLLIN event received).\n”, FIFO_DEVICE);
            // 读取数据
            ret = read(fd, buf, BUF_SIZE - 1);
            if (ret > 0) {
                buf[ret] = ‘\0’;
                printf(“Read %d bytes: [%s]\n”, ret, buf);
            } else if (ret == 0) {
                printf(“End of file.\n”);
            } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf(“Read would block (Non-blocking mode).\n”);
            } else {
                perror(“Read error”);
            }
        }
    }

    close(fd);
    return 0;
}

阻塞与非阻塞I/O核心对比

Linux系统编程与网络原理 中,阻塞访问与非阻塞访问是设备驱动与应用程序交互的两种基本模式,核心区别在于I/O条件不满足时的处理逻辑。

特性 阻塞访问 (Blocking I/O) 非阻塞访问 (Non-blocking I/O)
应用行为 等待。条件不满足时,进程暂停并进入休眠 立即返回。条件不满足时,进程立即返回错误-EAGAIN),不会休眠。
内核机制 基于等待队列,使用 wait_event_interruptible 休眠,wake_up_interruptible 唤醒。 基于轮询.poll方法)和 O_NONBLOCK 标志。
打开标志 默认模式,无需特殊标志。 需在 open() 中设置 O_NONBLOCK 标志。
性能/效率 CPU效率高:进程休眠不占CPU,适用于I/O较慢、并发需求不高的场景。 灵活性高:进程可继续其他工作,但需要应用层主动轮询或使用多路复用机制。
典型用途 简单串行任务;必须等待数据就绪的场景。 I/O 多路复用select/poll/epoll);高并发网络服务;需同时监控多个I/O流的场景,这也是解决 高并发场景下I/O效率 的关键技术之一。

实现要点总结:

  1. 阻塞访问:依赖 等待队列。驱动在 read/write 中调用 wait_event_interruptible() 使进程休眠,在条件满足时(如数据到达)调用 wake_up_interruptible() 唤醒进程。
  2. 非阻塞访问:依赖 O_NONBLOCK 标志.poll 方法。驱动检查 filp->f_flags & O_NONBLOCK,条件不满足则返回 -EAGAIN.poll 方法是支持用户空间 select/poll/epoll 多路复用的核心,实现“事件驱动”的轮询。

简而言之,阻塞访问让内核替你等待,而非阻塞访问则需要应用程序通过轮询机制主动判断I/O是否就绪。




上一篇:SpringBoot整合FFmpeg与ZLMediaKit实战指南:实现本地视频推流与实时直播
下一篇:Java 8 Optional与异常处理实战指南:规避空指针与代码冗余
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-7 01:45 , Processed in 0.070071 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表