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. javascript 编程技巧

    1.巧用判断: 在js中,NaN,undefined,Null,0,"" 在转换为bool的时候,是false,所以,可以这样写. if(!obj) {} 表示一个对象如果为fal ...

  2. 51nod 平均数(二分+树状数组)

    题目链接: 平均数 基准时间限制:4 秒 空间限制:131072 KB 分值: 80 LYK有一个长度为n的序列a. 他最近在研究平均数. 他甚至想知道所有区间的平均数,但是区间数目实在太多了. 为了 ...

  3. Unity3d之动态连接Mesh Renderer和Collider

    using UnityEngine; using System.Collections; public class dynaMesh : MonoBehaviour {     public Skin ...

  4. 【转载】理解OAuth 2.0

    http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 作者: 阮一峰 日期: 2014年5月12日 OAuth是一个关于授权(authorizat ...

  5. C#去除HTML标签(转)

    public static string ReplaceHtmlTag(string html, int length = 0) { string strText = System.Text.Regu ...

  6. 零基础Android学习笔记-02 安卓程序生命周期

    一个安卓程序生命周期会经历7中状态,并不一定是每次都全部经历.Create,Start,ReStart,Pause,Resume,Stop,Destory. 重载方法,用helloWorld程序去体验 ...

  7. 集合类学习之HashMap

    一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap  ...

  8. Cocos移植到Android的一些问题-中文乱码问题

    Android平台版本和设备碎片化很严重,因此从Win32平台移植到Android平台会有很多问题,下面是我们归纳的从Win32平台移植到Android平台遇到的一些问题.在Android平台中文乱码 ...

  9. 全选按钮的设定和POST处理当前循环的列表

    以下为全选按钮的代码,通过class实现. //全选按钮 $(function () { $("#selectall").change(function () { var isch ...

  10. JavaScript 弹窗

    JavaScript 弹窗 可以在 JavaScript 中创建三种消息框:警告框.确认框.提示框. 警告框 警告框经常用于确保用户可以得到某些信息. 当警告框出现后,用户需要点击确定按钮才能继续进行 ...