linux poll机制分析(二)

一、回顾

linux poll机制使用(一)写了个实现poll机制的简单例子。在驱动模块中需要实现struct file_operations.poll成员。在驱动模块中xxx_poll函数的的作用是将当前进程添加到等待队列中;然后判断事件是否发生,发生则返回POLLIN | POLLRDNORM,否则返回0(可以看看上一章的例子);接下来分析一下 linux 内核中 poll 机制的实现。

二、poll机制分析

1、系统调用

当应用层调用poll函数时,linux发生系统调用(系统调用入口CALL(sys_poll)),程序从应用空间切换到内核空间,然后执行sys_poll函数(sys_poll函数在fs\select.c文件中)。sys_poll 函数的作用是 根据时间计算滴答频率数,然后调用do_sys_poll函数。代码块如下:

/* 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函数

do_sys_poll函数在fs\select.c文件中;do_sys_poll函数的主要作用初始化table(table->pt->qproc = __pollwait),然后初始化poll链表(poll链表的作用是存放用户空间的struct pollfd)接着调用do_poll函数。下面的代码块省略了部分源代码。代码块如下:

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函数

do_sys_poll函数在fs\select.c文件中。do_poll 函数的作用主要是设置当前进程的任务状态,然后遍历poll链表、调用do_pollfd函数,然后根据实际情况是否将当前进程休眠或者唤醒当前进程,代码块如下:

//参数说明
/*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_pollfd函数在fs\select.c文件中,函数的作用就是,根据文件描述符找到struct file结构体,然后调用驱动程序的poll函数

//参数说明
/* 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;
}

三、总结

  1. poll > sys_poll > do_sys_poll > poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。
  2. 接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数
    它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;
    它还判断一下设备是否就绪
  3. 如果设备未就绪,do_sys_poll里会让进程休眠一定时间
  4. 进程被唤醒的条件有两个 :1、“一定时间”到了 ;2、二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。
  5. 如果驱动程序没有去唤醒进程,那么chedule_timeout(__timeou)超时后,会重复2、3动作,直到应用程序的poll调用传入的时间到达。

原文地址:https://www.cnblogs.com/gulan-zmc/p/12241004.html

时间: 2024-11-14 01:08:41

linux poll机制分析(二)的相关文章

Linux通信之poll机制分析

poll机制分析 韦东山 2009.12.10 所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open.sys_read.sys_write.sys_poll. 一.内核框架: 对于系统调用poll或select,它们对应的内核函数都是sys_poll.分析sys_poll,即可理解poll机制. sys_poll函数位于fs/select.c文件中,代码如下:

android 休眠唤醒机制分析(二) — early_suspend

本文转自:http://blog.csdn.net/g_salamander/article/details/7982170 early_suspend是Android休眠流程的第一阶段即浅度休眠,不会受到wake_lock的阻止,一般用于关闭lcd.tp等设备为运行的应用节约电能.Android的PowerManagerService会根据用户的操作情况调整电源状态,如果需要休眠则会调用到HAL层的set_screen_state()接口,在set_screen_state()中会向/sys/

Linux之poll机制分析

应用程序访问1个设备文件时可用阻塞/非阻塞方式.如果是使用阻塞方式,则直接调用open().read().write(),但是在驱动程序层会判断是否可读/可写,如果不可读/不可写,则将当前进程休眠,直到被唤醒.如果是使用非阻塞方式,就需要采用poll/select机制,而且打开文件时标记文件的访问权限位为O_NONBLOCK. 1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct t

linux poll机制使用(一)

一.poll机制的作用 1.poll机制的作用 在前面的使用中断的的方式来读取按键值(linux 中断管理(四)).使用这种方式读取按键,如果按键没有按下的时候,应用程序会一直处于睡眠的状态.如果想要即使按键没有按下,在一定的时间后也能返回,要实现这种功能,可以使用poll机制.(select IO复用和epoll也可以实现这种功能,这里只写poll机制) 二.poll机制的应用编程 1.应用层函数接口 1).API: int poll(struct pollfd *fds, nfds_t nf

poll机制分析[转]

所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open.sys_read.sys_write.sys_poll. 一.内核框架: 对于系统调用poll或select,它们对应的内核函数都是sys_poll.分析sys_poll,即可理解poll机制. sys_poll函数位于fs/select.c文件中,代码如下: asmlinkage long sys_pol

Linux poll机制

所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open.sys_read.sys_write.sys_poll. 一.内核框架:对于系统调用poll或select,它们对应的内核函数都是sys_poll.分析sys_poll,即可理解poll机制.1.sys_poll函数位于fs/select.c文件中,代码如下:asmlinkage long sys_poll(stru

linux信号机制分析

[摘要]本文分析了Linux内核对于信号的实现机制和应用层 的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执行 及注销等.最后介绍了应用层的相关处理,主要包括信号处理函数的安装.信号的发送.屏蔽阻塞等,最后给了几个简单的应用实例. [关键字]软中断信号,signal,sigaction,kill,sigqueue,settimer,sigmask,sigprocmask,sigset_t 1      

【转】Linux Writeback机制分析

1. bdi是什么? bdi,即是backing device info的缩写,顾名思义它描述备用存储设备相关描述信息,这在内核代码里用一个结构体backing_dev_info来表示. bdi,备用存储设备,简单点说就是能够用来存储数据的设备,而这些设备存储的数据能够保证在计算机电源关闭时也不丢失.这样说来,软盘存储设备.光驱存储设备.USB存储设备.硬盘存储设备都是所谓的备用存储设备(后面都用bdi来指示),而内存显然不是 2. bdi工作模型 相对于内存来说,bdi设备(比如最常见的硬盘存

Linux嵌入式驱动学习之路(十二)按键驱动-poll机制

实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键信息,则自己退出. 首先应用程序执行poll函数 kernel中的sys_poll do_sys_poll init_poll_funcptr-->do_poll do_poll for(;;) { if(do_pollfd(pfd,pt)) { count++; //如果驱动的poll返回非0值,