【问题标题】:in linux char device driver, what does the poll_queue_proc function do?在 linux char 设备驱动程序中,poll_queue_proc 函数是做什么的?
【发布时间】:2020-04-30 02:41:40
【问题描述】:

linux中有一个同步轮询多个设备文件的概念,我正在尝试了解它是如何工作的。
在 linux 2.6.23 源驱动程序/char/random.c 中,我看到以下代码

static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);

static unsigned int
random_poll(struct file *file, poll_table * wait)
{
    unsigned int mask;

    poll_wait(file, &random_read_wait, wait);
    poll_wait(file, &random_write_wait, wait);
    mask = 0;
    if (input_pool.entropy_count >= random_read_wakeup_thresh)
        mask |= POLLIN | POLLRDNORM;
    if (input_pool.entropy_count < random_write_wakeup_thresh)
        mask |= POLLOUT | POLLWRNORM;
    return mask;
}

poll_table 在 include/linux/poll.h 中定义如下

typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);

typedef struct poll_table_struct {
    poll_queue_proc qproc;
} poll_table;

我在一本书(第 5 章,Essential Linux Device Drivers,Venkateswaran)中看到“poll_table 是轮询数据的设备驱动程序所拥有的等待队列表”。但消息来源说它只是一个函数指针。而且我找不到这个函数 qproc 在做什么。 下面是在include/linux/poll.h中定义的函数poll_wait。

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
    if (p && wait_address)
        p->qproc(filp, wait_address, p);
}

在书中它说(关于鼠标的示例字符驱动程序),“mouse_poll() 使用库函数 poll_wait() 将等待队列 (mouse_wait) 添加到内核 poll_table 并进入睡眠状态。 "所以 poll_wait 可以休眠,但是在上面的 random_poll() 函数中,我们看到了两个连续的 poll_wait 函数。那么 random_poll 是否按顺序轮询读写可用性并将掩码发送到应用程序?如果有人可以向我展示 poll_queue_proc 函数的示例,我将不胜感激。我在linux驱动源码中找不到(应该只出现在应用程序中吗?)。

【问题讨论】:

    标签: linux linux-device-driver polling device-driver


    【解决方案1】:

    drivers/char/random.c:random_poll() 在用户空间调用时被调用 select()(或poll()epoll_wait())与文件 引用/dev/random的描述符。

    这些系统调用是事件多路复用的基础。在里面 下面的程序,用户空间打开了一些输入源(比如 /dev/random/dev/ttyS4) 并在两者上调用 select() 它们 阻塞,直到其中任何一个 具有要读取的输入数据。 (那里 是输入以外的其他事件源,输入只是最简单的。)

    #include <sys/select.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    #define _SYSE(ret, msg) do {                    \
        if (ret == -1) {                            \
            perror(msg);                            \
            exit(EXIT_FAILURE);                     \
        }                                           \
    } while (0)
    
    static int /*bool: EOF detected*/ consume_fd(int fd, const char* msg)
    {
        char tmp[64];
        ssize_t nread;
    
        nread = read(fd, tmp, sizeof(tmp));
        _SYSE(nread, "read");
        if (nread == 0 /*EOF*/)
            return 1;
    
        printf("%s: consumed %ld bytes\n", msg, nread);
        return 0;
    }
    
    int main(void)
    {
        int random_fd, tty_fd, nfds = 0;
    
        random_fd = open("/dev/random", O_RDONLY);
        _SYSE(random_fd, "open random");
        if (random_fd > nfds)
            nfds = random_fd+1;
    
        tty_fd = open("/dev/ttyS4", O_RDONLY);
        _SYSE(tty_fd, "open tty");
        if (tty_fd > nfds)
            nfds = tty_fd+1;
    
        while (1) {
            fd_set in_fds;
            int ret;
    
            FD_ZERO(&in_fds);
            FD_SET(random_fd, &in_fds);
            FD_SET(tty_fd, &in_fds);
    
            ret = select(nfds, &in_fds, NULL, NULL, NULL);
            _SYSE(ret, "select");
    
            if (FD_ISSET(random_fd, &in_fds)) {
                int eof_detected = consume_fd(random_fd, "random");
                if (eof_detected) 
                    break;
            }
            if (FD_ISSET(tty_fd, &in_fds)) {
                int eof_detected = consume_fd(tty_fd, "tty");
                if (eof_detected) 
                    break;
            }
        }
        return 0;
    }
    

    一旦随机数可用,或者 串行线有数据。 (请注意,现在/dev/random 不 块,而是生成伪随机数,所以输出真的是 快。)

    select() 调用进入内核时,random_poll() 被调用,另一个类似的函数在 TTY 的某处 层 - 仅仅因为 select() 将这些文件描述符传递为 参数。这些函数应该简单地将调用者入队 进入您无法触及的poll_table(它 表示为此目的的调用任务)。

    在第二阶段,select() 的实现会暂停 调用者,直到任何事件变为真。 (见fs/select.c。)

    【讨论】:

    • 我明白了,您的解释和示例对我有很大帮助,谢谢!所以看起来它通过了函数, (system call sys_ppoll) -> do_sys_poll -> do_pol l-> do_polfd ->(f_op->poll) 。我在 do_sys_poll 中找到了 poll_initwait(..) 并且 qproc 函数被初始化为 __pollwait 函数。 and__pollwait 函数似乎将进程放入等待队列。啊,linux系统太复杂了:)
    猜你喜欢
    • 2020-09-06
    • 2010-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多