您的位置:首页 > 其它

epoll源码分析(三)

2016-05-11 17:26 323 查看
 epoll源码分析(三) http://blog.chinaunix.net/uid-20687780-id-2105159.html

分类: LINUX

epoll_wait系统实现如下:

asmlinkage long sys_epoll_wait(int epfd,struct
epoll_event __user *events,

            int maxevents,int timeout)

{

    int error;

    struct file *file;

    struct eventpoll *ep;

    //#define EP_MAX_EVENTS (INT_MAX / sizeof(struct
epoll_event))

    //178956970(1.7亿)

    if(maxevents <=0 || maxevents > EP_MAX_EVETNS) 

        return -EINVAL;

    //判断返回事件数组是否合法

    if(!access_ok(VERIFY_WRITE,events,

            maxevents * sizeof(struct epoll_event)))

    {

        error = -EFAULT;

        goto error_return;

    }

    error = -EBADF;

    file = fget(epfd);

    

    if(!file)

        goto error_return;

    error = -EINVAL;

    if(!is_file_epoll(file))

        goto error_fput;

    //将epoll注册时设置的数据结构取出来,开始进行判断

    ep = file->private_data;

    error = ep_poll(ep,events,maxevents,timeout);

        ….......

}

现在又转入了ep_poll函数中:

static int ep_poll(struct eventpoll *ep,struct
epoll_event __user *events,

                int maxevents,long timeout)

{

    int res,avail;

    unsigned long flags;

    long jtimeout;

    wait_queue_t wait;

    

    //注册的0ms按0.999 Jiffies处理,并非真正的0s,HZ=100,

    //jiffies/HZ 为s

    jtimeout = (timeout<0 || timeout >= EP_MAX_MSTIMEO)?

        MAX_SCHEDULE_TIMEOUT:(timeout*HZ+999)/1000;

retry:

    spin_lock_irqsave(&ep->lock,flags);

    

    res = 0;

    //事件就绪队列为空,就监听poll

    if(list_empty(&ep->rdllist))

    {

        //让当前进程挂在等待队列wait上,并将该等待队列加入到ep->wq(epoll_wait的            专属队列中),

        init_waitqueue_entry(&wait,current);

        wait.flags |= WQ_FLAG_EXCLUSIVE;

        __add_wait_queue(&ep->wq,&wait);

        for(;;){

            //进程设置睡眠状态,等到信息时变唤醒

            set_current_state(TASK_INTERRUPTIBLE);

            if(!list_empty(&ep->rdllist) || !jtimeout)//只要事件到来,就返回

                break;

            if(signal_pending(current)) {//被信号中断就会返回

                res = -EINTR;

                break;

            }

        spin_unlock_irqrestore(&ep->lock,flags);

        //进程进入睡眠状态直到规定的睡眠事件醒来或者注册的fd对应的poll驱动函数唤醒该            进程

        jtimeout = schedule_timeout(jtimeout);

        spin_lock_irqrestore(&ep->lock,flags);

        }

    //poll驱动唤醒了该进程,现在就将对应的poll从等待队列中清除出去,并设置为运行状态

    __remove_wait_queue(&ep->wq,&wait);

    set_current_state(TASK_RUNNING);

    }

    eavail = !list_empty(&ep->rdllist);

    spin_unlock_irqrestore(&ep->lock,flags);

    //没有被中断,有就绪事件,并且向用户空间发送成功,就返回

    if(!res && eavail && !(res = ep_send_events(ep,events,maxevents))

        &&jtimeout)

        goto retry;

    return res;

}

ep_send_events函数向用户空间发送就绪事件:

static int ep_send_events(struct eventpoll *ep,struct
epoll_event __user *events,int maxevents)

{

    int eventcnt,error = -EFAULT,pwake = 0;

    unsigned int revents;

    unsigned long flags;

    struct epitem *epi,*nepi;

    struct list_head txlist;

    INIT_LIST_HEAD(&txlist);

    mutex_lock(&ep->mtx);

    spin_lock_irqsave(&ep->lock,flags);

    //将ep->rdllist链表加入到txlist链表中去,这样的话rdllist链表就为空了

    list_splice(&ep->rdllist,&txlist);

    INIT_LIST_HEAD(&ep->rdllist);

    ep->ovflist = NULL;

    spin_unlock_irqrestore(&ep->lock,flags);

    //将rdllist链表中的每一项都发送至用户空间

    for(eventcnt = 0; !list_empty(&txlist) && eventcnt < maxevents;) {

        

        epi = list_first_entry(&txlist,struct
epitem,rdllink);

        list_del_init(&epi->rdllink);    

        //立刻返回当前文件的就绪事件

        revents = epi->ffd.file->f_op->poll(epi->ffd.file,NULL);

        revents &= epi->event.events;

        

        if(revents) {

            //将就绪事件的poll_event发送至用户空间

            if(__put_user(revents,&events[eventcnt.].events) ||

             __put_user(epi->event.data,&events[eventcnt].data))

                

                goto errxit;

            //#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)

            if(epi->event.events & EPOLLONESHOT)

                epi->event.events &= EP_PRIVATE_BITS;

            eventcnt++;

        }

     //非边缘触发,且事件就绪时,就将epi->rdllink加入到rdllist链表中,实际上就是将没有标记为ET模式的fd又放回到rdllist中,这样下次就绪时又能将其发送至用户空间了

     if(!(epi->event.events & EPOLLET) && (revents & 

                epi->event.events))

            list_add_tail(&epi->rdllink,&ep->rdllist);

}

    error = 0;

errixt:

    spin_lock_irqsave(&ep->lock,flags);

    //在执行上面的代码期间,又有可能有就绪事件,这样的话就进入了ovflist队列,这样有需要再一次确认一次    

    for(nepi = ep->ovflist;(epi = nepi)!= NULL;

     nepi = epi->next;epi->next = EP_UNACTIVE_PTR) {

        //链表为空且没有ET事件发生,#define
EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET),这里也和上面的一样

        if(!ep_is_linked(&epi->rdllink) && (epi->event.events & 

            ~EP_PRIVATE_BITS))

            //又将rdllink其加入到rdllist中

                list_add_tail(&epi->rdllink,&ep->rdllist);

    }

    //#define EP_UNACTIVE_PTR    ((void*) -1L)

    ep->ovflist = EP_UNACTIVE_PTR;

    list_spice(&txlist,&ep->rdllist);//现在又将txlist链表加入到rdllist链表中去

    if(!list_empty(&ep->rdllist))

    {

        //等待的队列不为空

        if(waitqueue_active(&ep->wq))

            

            __wake_up_locked(&ep->wq,TASK_UNINTERRUPTIBLE |

            TASK_INTERRUPTIBLE);

        //如果poll队列不为空,则唤醒的次数加1

        if(waitqueue_active(&ep->poll_wait))

            pwake++;

    }

    spin_unlock_irqrestore(&ep->lock,flags);

    mutex_unlock(&ep->mtx);

    if(pwake)

        ep_poll_safewake(&psw,&ep->poll_wait);

    return eventcnt == 0?error:eventcnt;

}

这样epoll_wait的调用顺序为:





参考资料:

linux-2.6.24.3源代码

http://donghao.org/2009/08/linuxiapolliepollaueouaeaeeio.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: