linux poll Mechanism (b)

I. Review

In linux poll mechanism uses (a) wrote a simple example of an implementation mechanism of the poll. In the drive modules need to implement struct file_operationsa .pollmember. In the drive module xxx_poll函数role is to add to the current process waiting queue; and then judge whether the event occurred, occurred return POLLIN | POLLRDNORM, otherwise it returns 0 (you can look at the example of the previous chapter); Then analyze the linux kernel poll mechanism implementation.

Two, poll Mechanism

1, the system calls

When the application calls the poll function layer, linux generation system call ( 系统调用入口CALL(sys_poll)), the program is switched from the application space to the kernel space, and then perform sys_pollthe function (sys_poll function in the fs\select.cfile). sys_pollFunction returns the number of frequency is calculated based on the time tick, and then calls do_sys_pollthe function. Code block is as follows:

/* ufds:应用层传递过来的struct pollfd结构体数组
 * nfds:应用层传递过来的struct pollfd结构体个数
 * timeout_msecs:超时时间*/
asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs)
{
    s64 timeout_jiffies;
    if (timeout_msecs > 0) {
        /* 大于0,根据时间计算滴答频率数 */
        timeout_jiffies = msecs_to_jiffies(timeout_msecs);
    } else {
        /* Infinite (< 0) or no (0) timeout */
        timeout_jiffies = timeout_msecs;
    }
    /* 然后调用do_sys_poll函数*/
    return do_sys_poll(ufds, nfds, &timeout_jiffies);
}

2, do_sys_poll function

do_sys_pollFunction in the fs\select.cfile; do_sys_pollprimary role initialization function Table (table->pt->qproc = __pollwait), then initialized poll链表(action poll list is located in user space struct pollfd) then calls do_pollthe function. The following code block is omitted part of the source code. Code block is as follows:

int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
{
    struct poll_wqueues table;
    struct poll_list *head;
    struct poll_list *walk;
    .....//省略了部分源代码
    
    if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
       return -EINVAL;


    /* 初始化 table */
    /* table->pt->qproc =  __pollwait
     * __pollwait 主要将当前进程挂到等待队列中,这里只是做初始化而已。
     * 在驱动程序里调用的 poll_wait(file, &button_waitq, wait); 函数里面最终
     * 调用的就是table->pt->qproc()函数,也就是调用__pollwait()函数。
     */
    poll_initwait(&table);
    

    .....//省略部分源代码(省略的源代码其实就是分配空间和初始化head链表)
    


    /* do_poll函数的参数说明:
     *
     * nfds: 应用层传递过来的struct pollfd结构体个数
     * head: poll链表头,链表里面包含应用层传进来的struct pollfd数组的信息
     * table: table->pt->qproc指向__pollwait函数(主要将当前进程挂到等待队列中)
     * timeout: 等待超时
     */
    /* 调用do_poll函数 */
    fdcount = do_poll(nfds, head, &table, timeout);
    
    .....//省略部分源代码(这部分的源代码是将revents(返回的事件) copy 到应用层)
    return err;
}

3, do_sys_poll function

do_sys_pollFunction in the fs\select.cfile. do_pollThe main function of the role is to set the current process task status, and then traverse the poll list, call the do_pollfdfunction, and then whether the current process to sleep or wake up the current process based on the actual situation, code blocks as follows:

//参数说明
/*nfds: 应用层传递过来的struct pollfd结构体个数
 *list:poll链表头,链表里面包含应用层传进来的struct pollfd数组的信息
 *wait:wait->pt->qproc指向__pollwait函数(主要将当前进程挂到等待队列中)
 *timeout:等待超时时间
 */
static int do_poll(unsigned int nfds,  struct poll_list *list,
          struct poll_wqueues *wait, s64 *timeout)
{
    int count = 0;
    poll_table* pt = &wait->pt;
    
    /* Optimise the no-wait case */
    if (!(*timeout))
        pt = NULL;
     /* 执行poll的时候有一个大循环 */
    for (;;) {
        struct poll_list *walk;
        long __timeout;
        /* 设置当前的任务状态为可中断休眠 */
        set_current_state(TASK_INTERRUPTIBLE);
        /* 遍历poll链表 */
        for (walk = list; walk != NULL; walk = walk->next) {
            struct pollfd * pfd, * pfd_end;
            /* 获取头部的地址 */
            pfd = walk->entries;
            /* 获取尾部指针 */
            pfd_end = pfd + walk->len;
            /* 遍历struct pollfd结构体数组 */
            for (; pfd != pfd_end; pfd++) {
              
                /*------------------------------------------------------------------*/
                 /* do_pollfd其实就是调用开发者所写的xxx_poll函数了(里面调用的的是struct file_operations .poll函数)
                  * do_pollfd函数下面的代码块分析
                  */
                  
                 /* 执行完do_pollfd后,这个进程就被挂到等待队列里面了。
                  * 这里仅仅是挂到等待队列而已,进程的休眠是在下面
                  * 执行 schedule_timeout 函数的时候休眠的
                  */
                 
                if (do_pollfd(pfd, pt)) {
                    /* 如果执行驱动程序的poll返回的是非0值 则count++ */
                    /* 表名设备有数据要返回给应用程序 */
                    count++;
                    pt = NULL;
                }
                /*------------------------------------------------------------------*/
            }
        }
        
        pt = NULL;
        /* 结束大循环的条件有三个:
         * count: 表示设备有数据返回给应用程序
         * timeout: 等待超时时间到
         * signal_pending(current): 当前进程接收到信号
         */
        if (count || !*timeout || signal_pending(current))
            break;
        /* 发生错误退出 */
        count = wait->error;
        if (count)
            break;
        if (*timeout < 0) {
            /* Wait indefinitely */
            __timeout = MAX_SCHEDULE_TIMEOUT;
        } else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) {
            /*
             * Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in
             * a loop
             */
            __timeout = MAX_SCHEDULE_TIMEOUT - 1;
            *timeout -= __timeout;
        } else {
            __timeout = *timeout;
            *timeout = 0;
        }
        /* 执行到这里之后 timeout 的值已经为零,下一次循环就会超时返回 */
        
        
        /* 这里让当前进程休眠一会
         * 唤醒进程的处理休眠的时间到之外,还可以由驱动程序唤醒
         */
        __timeout = schedule_timeout(__timeout);
        if (*timeout >= 0)
            *timeout += __timeout;
    }
    /* 将当前进程设置为就绪状态,等待CPU调度 */
    __set_current_state(TASK_RUNNING);
    return count;
}

4、do_pollfd

do_pollfdFunction in the fs\select.cfile, the function of the role is, to find the file descriptor struct filestructure, and then calls the driver'spoll函数

//参数说明
/* pollfd: struct pollfd结构体
 * pwait:pwait->qproc指向__pollwait函数(主要将当前进程挂到等待队列中)
 */
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
{
    unsigned int mask;
    int fd;
    mask = 0;
    fd = pollfd->fd;
    if (fd >= 0) {
        int fput_needed;
        struct file * file;
        /* 根据文件描述符,获取file结构
         * file结构是每打开一个文件就会有一个file
         */
        file = fget_light(fd, &fput_needed);
        mask = POLLNVAL;
        if (file != NULL) {
            mask = DEFAULT_POLLMASK;
            if (file->f_op && file->f_op->poll)
                /* 调用驱动里面的poll函数,这个函数是驱动开发者添加的poll函数 */
                /* 如果驱动有数据让应用程序读的话,就返回非0, 否侧返回0 */
                mask = file->f_op->poll(file, pwait);
            /* Mask out unneeded events. */
            /* 根据应用层传递的events,来屏蔽不需要的事件 */
            mask &= pollfd->events | POLLERR | POLLHUP;
            fput_light(file, fput_needed);
        }
    }
    pollfd->revents = mask;
    /* 设备有数据可读则返回非0 */
    /* 否则返回0 */
    return mask;
}

Third, the summary

  1. poll> sys_poll> do_sys_poll> poll_initwait, poll_initwait callback function registered at __pollwait, it is when our driver performs poll_wait, real function is called.
  2. Next to perform file-> f_op-> poll, that we are in the driver poll function own implementation of
    it will call poll_wait hang himself into a queue which is driving our own definition;
    it also determine what equipment is ready
  3. If the device is not ready, do_sys_poll process will sleep in a certain time
  4. Process to be awakened two conditions: 1, "some time" to; 2, the second is to be awakened driver. When the driver found the conditions in place, put the hanging of the wake-up process, "a queue", the queue is put in front of this process by poll_wait hang past the queue.
  5. If the driver does not have to wake up process, then chedule_timeout (__ timeou) after a timeout, repeat 2,3 action until the calling application poll incoming time of arrival.

Guess you like

Origin www.cnblogs.com/gulan-zmc/p/12241004.html