struct pollfd {
    int fd;     //当前描述符
    short events;     //进程关心的该描述符的事件
    short revents;    //返回的事件
};

asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,
            long timeout_msecs)
{
    s64 timeout_jiffies;

//超时时间处理
    if (timeout_msecs > 0) {
#if HZ > 1000
        /* We can only overflow if HZ > 1000 */
        if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ)
            timeout_jiffies = -1;
        else
#endif
            timeout_jiffies = msecs_to_jiffies(timeout_msecs);
    } else {
        /* Infinite (< 0) or no (0) timeout */
        timeout_jiffies = timeout_msecs;
    }

//实际处理函数
    return do_sys_poll(ufds, nfds, &timeout_jiffies);
}

struct poll_list {
    struct poll_list *next;
    int len;
    struct pollfd entries[0];
};

int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
{
    struct poll_wqueues table;
     int fdcount, err;
     unsigned int i;
    struct poll_list *head;
     struct poll_list *walk;
    /* Allocate small arguments on the stack to save memory and be
       faster - use long to make sure the buffer is aligned properly
       on 64 bit archs to avoid unaligned access */
    long stack_pps[POLL_STACK_ALLOC/sizeof(long)]; //栈的分配会更快
    struct poll_list *stack_pp = NULL;

//检查描述符个数是否超过系统的限制
    /* Do a sanity check on nfds ... */
    if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
        return -EINVAL;

//首先是一个初始化工作, 主要初始化poll_table这个函数指针
    poll_initwait(&table);

head = NULL;
    walk = NULL;
    i = nfds;
    err = -ENOMEM;
    //这个循环所作的工作就是将从用户传过来的多个pollfd结构信息拷贝到内核,
        //由于可能结构的个数可能超过一页内存所能存储的范围,所以就用了循环来完成,
        //每次拷贝一页内存能装载的个数。并且再将它们用链表链起来。
    while(i!=0) {
        struct poll_list *pp;
        int num, size;
        if (stack_pp == NULL)
            num = N_STACK_PPS;
        else
            num = POLLFD_PER_PAGE; //这里保证kmalloc分配的空间不会超过一个页面
        if (num > i)
            num = i;
        size = sizeof(struct poll_list) + sizeof(struct pollfd)*num;

//如果描述符的个数比较小时,或在比较大的时候,第一次会使用栈来存储
        if (!stack_pp)
            stack_pp = pp = (struct poll_list *)stack_pps;
        else {
            pp = kmalloc(size, GFP_KERNEL);
            if (!pp)
                goto out_fds;
        }
        pp->next=NULL;
        pp->len = num;
        if (head == NULL)
            head = pp;
        else
            walk->next = pp;

walk = pp;
        if (copy_from_user(pp->entries, ufds + nfds-i,
                sizeof(struct pollfd)*num)) {
            err = -EFAULT;
            goto out_fds;
        }
        i -= pp->len;
    }

//真正的POLL操作,返回的结果在head中
    fdcount = do_poll(nfds, head, &table, timeout);

//双重循环,将事件拷贝回给用户空间
    /* OK, now copy the revents fields back to user space. */
    walk = head;
    err = -EFAULT;
    while(walk != NULL) {
        struct pollfd *fds = walk->entries;
        int j;

for (j=0; j < walk->len; j++, ufds++) {
            if(__put_user(fds[j].revents, &ufds->revents))
                goto out_fds;
        }
        walk = walk->next;
    }
    err = fdcount;
    if (!fdcount && signal_pending(current))
        err = -EINTR;

//以下是释放空间
out_fds:
    walk = head;
    while(walk!=NULL) {
        struct poll_list *pp = walk->next;
        if (walk != stack_pp)
            kfree(walk);
        walk = pp;
    }
    poll_freewait(&table);
    return err;
}

//这个函数就是将当前进程加入等待队列,这个等待队列由驱动或文件系统或网络协议栈来提供
//这个函数是由驱动的file->poll中调用poll_wait()来间接调用的。
/* Add a new entry */
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
                poll_table *p)
{
    struct poll_table_entry *entry = poll_get_entry(p);
    if (!entry)
        return;
    get_file(filp);
    entry->filp = filp;
    entry->wait_address = wait_address;
    init_waitqueue_entry(&entry->wait, current);
    add_wait_queue(wait_address,&entry->wait);
}

void poll_initwait(struct poll_wqueues *pwq)
{
    //在poll()中初始化为__pollwait(),注意在epoll中又会不同
    init_poll_funcptr(&pwq->pt, __pollwait);
    pwq->error = 0;
    pwq->table = NULL;
    pwq->inline_index = 0;
}

===========================================

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;

for (;;) {
        struct poll_list *walk;
        long __timeout;

也是一个双重循环,处理每个文件描述符事件
        set_current_state(TASK_INTERRUPTIBLE);
        for (walk = list; walk != NULL; walk = walk->next) {
            struct pollfd * pfd, * pfd_end;

pfd = walk->entries;
            pfd_end = pfd + walk->len;
            for (; pfd != pfd_end; pfd++) {
                /*
                * Fish for events. If we found one, record it
                * and kill the poll_table, so we don't
                * needlessly register any other waiters after
                * this. They'll get immediately deregistered
                * when we break out and return.
                */
                if (do_pollfd(pfd, pt)) { //处理每个文件描述符
                    count++;
                    pt = NULL;
                }
            }
        }

//超时处理
        /*
        * All waiters have already been registered, so don't provide
        * a poll_table to them on the next loop iteration.
        */
        pt = NULL;
        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 = schedule_timeout(__timeout);
        //进程被唤醒, 继续执行
        if (*timeout >= 0)
            *timeout += __timeout;
    }
    __set_current_state(TASK_RUNNING);
    return count;
}

/*
* Fish for pollable events on the pollfd->fd file descriptor. We're only
* interested in events matching the pollfd->events mask, and the result
* matching that mask is both recorded in pollfd->revents and returned. The
* pwait poll_table will be used by the fd-provided poll handler for waiting,
* if non-NULL.
*/
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 = fget_light(fd, &fput_needed);
        mask = POLLNVAL;
        if (file != NULL) {
            mask = DEFAULT_POLLMASK;

//调用驱动或文件系统的poll函数, 是否将当前进程加入驱动的等待队列,
                      //取决是file->poll()第二个参数是否为空.
            if (file->f_op && file->f_op->poll)
                mask = file->f_op->poll(file, pwait);
            /* Mask out unneeded events. */
            mask &= pollfd->events | POLLERR | POLLHUP;
            fput_light(file, fput_needed);
        }
    }
    pollfd->revents = mask; //更新参数返回值

return mask; //如果可读/写返回非0值
}

=================================
驱动或文件系统的poll()实现原型:
test_poll(struct file *filep, poll_table *wait)
{
    ...
    poll_wait(filep, &dev->wait_queue_head, wait);
    ...

if (dev->readable)
        mask |= POLLIN | POLLRDNORM;

if (dev->writable)
        mask |= POLLOUT | POLLWRNORM;

...

}

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); //这个函数就是上面又poll_initwait()初始化的__pollwait()了.
}

poll实现的更多相关文章

  1. select、poll、epoll之间的区别总结

    select.poll.epoll之间的区别总结 05/05. 2014 select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪 ...

  2. (转载) Linux IO模式及 select、poll、epoll详解

    注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案 ...

  3. linux下select/poll/epoll机制的比较

    select.poll.epoll简介 epoll跟select都能提供多路I/O复用的解决方案.在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSI ...

  4. select,epoll,poll比较

    介绍和比较 http://www.cnblogs.com/maociping/p/5132583.html 比较 http://www.dataguru.cn/thread-336032-1-1.ht ...

  5. linux poll函数

    poll函数与select函数差不多 函数原型: #include <poll.h> int poll(struct pollfd fd[], nfds_t nfds, int timeo ...

  6. poll机制

    使用POLL机制代替linux输入子系统(input subsystem)之按键输入和LED控制中的异步通知,实现同样的效果. 1.代码 只简单修改input_subsys_test.c, input ...

  7. select、poll、epoll之间的区别总结[整理]

    select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select ...

  8. IO多路复用之poll总结

    1.基本知识 poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制.poll和selec ...

  9. select、poll、epoll区别总结

    1 本质上都是同步I/O 三者都是I/O复用,本质上都属于同步I/O.因为三者只是负责通知应用程序什么时候数据准备好了,实际的I/O操作还是在由应用程序处理:如果是异步I/O的话,实际I/O由内核处理 ...

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

    实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键 ...

随机推荐

  1. PHP 错误处理机制

    在日常的项目开发过程中,总是会出现一些我们意想不到的异常错误,如果我们对此没有进行相对完善的处理,那么程序看上去也很不专业,也很可能就会成为别人攻击系统的有效信息:有些错误异常会终止脚本执行,这个时候 ...

  2. 文本的输入输出(page52)

    2.2.4 再谈输入输出, 所用java类有:Out.java , In.java public class Cat{ public static void main(String[] args){ ...

  3. 【求无向图的桥,有重边】ZOJ - 2588 Burning Bridges

    模板题——求割点与桥 题意,要使一个无向图不连通,输出必定要删掉的边的数量及其编号.求桥的裸题,可拿来练手. 套模板的时候注意本题两节点之间可能有多条边,而模板是不判重边的,所以直接套模板的话,会将重 ...

  4. 一分钟加入google站内搜索代码

    一分钟加入google站内搜索代码| 一分钟加入google站内搜索代码|只有7行最精简.网上有很多 google 站内搜索代码,但是出于某些目的,很多都加入了多余的代码,从seo的角度来讲,是很不优 ...

  5. 【CSS3】---属性选择器

    在HTML中,通过各种各样的属性可以给元素增加很多附加的信息.例如,通过id属性可以将不同div元素进行区分. 在CSS2中引入了一些属性选择器,而CSS3在CSS2的基础上对属性选择器进行了扩展,新 ...

  6. 破解软件感悟-PE文件格式之实例总结(五)

    有很多介绍PE文件的文章,但是我打算写一篇关于输入表的文章,因为它对于破解很有用.     我想解释它的最好的方法是举一个例子,你可以跟着我逐步深入,一步一步的思考,最后你将完全明白,我选择了一个我刚 ...

  7. NPOI--操作Excel之利器(一)

    最近在做一个产品配置的项目,类似于京东上的自主装机,也就是根据自己的需要配置一套完整的产品,只不过我们做的是一个网络产品的配置,如路由器,交换机等网络设备.配置完成后会将配置的信息导出到Excel中, ...

  8. Part 59 to 60 Difference between Convert ToString and ToString,String and StringBuilder

    Part 59 Difference between Convert ToString and ToString Part 60 Difference between String and Strin ...

  9. usb2.0 规范学习笔记

    1.一个USB HOST 最多可以同时支持128 个地址,地址0 作为默认地址,只在设备枚举期间临时使 用,而不能被分配给任何一个设备,因此一个USB HOST 最多可以同时支持127 个地址,如果一 ...

  10. 10款基于jquery实现的超酷动画源码

    1.jQuery二级下拉菜单 下拉箭头翻转动画 之前我们分享过不少基于jQuery的二级下拉菜单,甚至是多级的下拉菜单,比如这款jQuery/CSS3飘带状多级下拉菜单就非常华丽.但今天要介绍的这款j ...