Linux poll机制

1.用户空间调用(参考 poll(2) - Linux man page

 int poll(struct pollfd *fds, nfds_t nfds, int timeout);

it waits for one of a set of file descriptors to become ready to perform I/O.

The set of file descriptors to be monitored is specified in the fds argument, which is an array of structures of the following form:

struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};

关于timeout参数的说明:

  timeout>0,设置超时时间为timeout

  timeout=0,直接返回

  timeout<0,无限长超时时间

返回值说明:

On success, a positive number is returned; this is the number of structures which have nonzero revents fields (in other words, those descriptors with events or errors reported).

A value of 0 indicates that the call timed out and no file descriptors were ready. On error, -1 is returned, and errno is set appropriately.

2.内核调用

asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs)
    -->ret = do_sys_poll(ufds, nfds, &timeout);
        -->struct poll_wqueues table;
        -->poll_initwait(&table);
            -->init_poll_funcptr(&pwq->pt, __pollwait);
        -->把ufds指向的用户空间数据拷贝到内核空间,由poll_list结构体保存
        -->fdcount = do_poll(nfds, head, &table, timeout);
        for (;;) {
            -->set_current_state(TASK_INTERRUPTIBLE);
            -->for (walk = list; walk != NULL; walk = walk->next) //遍历poll_list结构中的pollfd结构
                -->do_pollfd(pfd, pt)
                    -->if (file->f_op && file->f_op->poll) //驱动中的poll函数
                        --> void __pollwait(struct file *filp, wait_queue_head_t *wait_address,poll_table *p)
                            -->struct poll_table_entry *entry = poll_get_entry(p);
                            -->entry->filp = filp;
                            -->entry->wait_address = wait_address;
                            -->init_waitqueue_entry(&entry->wait, current);
                            -->add_wait_queue(wait_address, &entry->wait);//加入等待队列头
                    -->return mask;//返回设备是否可读写状态
            -->if (count || !*timeout || signal_pending(current))
                break;//设备可读写,超时时间到,有信号中断三种情况都返回
            -->__timeout = schedule_timeout(__timeout);//进程调度,休眠
        }
            //唤醒后返回
            -->__set_current_state(TASK_RUNNING);
            -->return count;
        -->把poll_list结构体保存的pollfd.revents返回给用户空间的ufds数组
        -->poll_freewait(&table);//释放poll_table_entry中的等待队列

3.相关结构体都在fs/select.c,include/linux/poll.h

poll_wqueues是最关键的一个结构体,

 1 struct poll_wqueues {
 2     poll_table pt;
 3     struct poll_table_page * table;
 4     int error;
 5     int inline_index;
 6     struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
 7 };

poll_table结构体只包含一个函数指针,初始化时指向

void __pollwait(struct file *filp, wait_queue_head_t *wait_address,poll_table *p)

1 typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
2 
3 typedef struct poll_table_struct {
4     poll_queue_proc qproc;
5 } poll_table;

poll_table_page就是包含poll_table_entry结构的链表,作用与inline_entries[N]相同,检测文件数少的时候用不到这个结构

1 struct poll_table_page {
2     struct poll_table_page * next;
3     struct poll_table_entry * entry;
4     struct poll_table_entry entries[0];
5 };

poll_table_entry包含文件指针,等待队列及等待队列头

1 struct poll_table_entry {
2     struct file * filp;
3     wait_queue_t wait;
4     wait_queue_head_t * wait_address;
5 };

4.操作

poll_wqueues初始化

 1 static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
 2 {
 3     pt->qproc = qproc;
 4 }
 5 void poll_initwait(struct poll_wqueues *pwq)
 6 {
 7     init_poll_funcptr(&pwq->pt, __pollwait);
 8     pwq->error = 0;
 9     pwq->table = NULL;
10     pwq->inline_index = 0;
11 }

那么poll_table结构中的函数什么时候使用及干什么呢?

我们自己在驱动中的poll函数中会调用poll_wait()函数

 1 static unsigned forth_drv_poll(struct file *file, poll_table *wait)
 2 {
 3     unsigned int mask = 0;
 4     poll_wait(file, &button_waitq, wait); // 不会立即休眠
 5 
 6     if (ev_press)
 7         mask |= POLLIN | POLLRDNORM;
 8 
 9     return mask;
10 }

而poll_wait()函数最终会调用__pollwait()函数

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);
}
 1 /* Add a new entry */
 2 static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
 3                 poll_table *p)
 4 {
 5     struct poll_table_entry *entry = poll_get_entry(p);
 6     if (!entry)
 7         return;
 8     get_file(filp);
 9     entry->filp = filp;
10     entry->wait_address = wait_address; //即forth_drv_poll中传入的button_waitq等待队列头
11     init_waitqueue_entry(&entry->wait, current);
12     add_wait_queue(wait_address, &entry->wait);
13 }
原文地址:https://www.cnblogs.com/yangjiguang/p/7622780.html