epoll源码分析(三)

 epoll源码分析(三) http://blog.chinaunix.net/uid-20687780-id-2105159.html

分类: LINUX

epoll_wait系统实现如下:

  1. asmlinkage long sys_epoll_wait(int epfd,struct epoll_event __user *events,
  2.             int maxevents,int timeout)
  3. {
  4.     int error;
  5.     struct file *file;
  6.     struct eventpoll *ep;
  7.     //#define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event))
  8.     //178956970(1.7亿)
  9.     if(maxevents <=|| maxevents > EP_MAX_EVETNS) 
  10.         return -EINVAL;
  11.     //判断返回事件数组是否合法
  12.     if(!access_ok(VERIFY_WRITE,events,
  13.             maxevents * sizeof(struct epoll_event)))
  14.     {
  15.         error = -EFAULT;
  16.         goto error_return;
  17.     }

  18.     error = -EBADF;
  19.     file = fget(epfd);
  20.     
  21.     if(!file)
  22.         goto error_return;
  23.     error = -EINVAL;
  24.     if(!is_file_epoll(file))
  25.         goto error_fput;
  26.     //将epoll注册时设置的数据结构取出来,开始进行判断
  27.     ep = file->private_data;
  28.     error = ep_poll(ep,events,maxevents,timeout);
  29.         ….......
  30. }

现在又转入了ep_poll函数中:

  1. static int ep_poll(struct eventpoll *ep,struct epoll_event __user *events,
  2.                 int maxevents,long timeout)
  3. {
  4.     int res,avail;
  5.     unsigned long flags;
  6.     long jtimeout;
  7.     wait_queue_t wait;
  8.     
  9.     //注册的0ms按0.999 Jiffies处理,并非真正的0s,HZ=100,
  10.     //jiffies/HZ 为s
  11.     jtimeout = (timeout<|| timeout >= EP_MAX_MSTIMEO)?
  12.         MAX_SCHEDULE_TIMEOUT:(timeout*HZ+999)/1000;

  13. retry:
  14.     spin_lock_irqsave(&ep->lock,flags);
  15.     
  16.     res = 0;
  17.     //事件就绪队列为空,就监听poll
  18.     if(list_empty(&ep->rdllist))
  19.     {
  20.         //让当前进程挂在等待队列wait上,并将该等待队列加入到ep->wq(epoll_wait的            专属队列中),
  21.         init_waitqueue_entry(&wait,current);
  22.         wait.flags |= WQ_FLAG_EXCLUSIVE;
  23.         __add_wait_queue(&ep->wq,&wait);

  24.         for(;;){
  25.             //进程设置睡眠状态,等到信息时变唤醒
  26.             set_current_state(TASK_INTERRUPTIBLE);
  27.             if(!list_empty(&ep->rdllist) || !jtimeout)//只要事件到来,就返回
  28.                 break;
  29.             if(signal_pending(current)) {//被信号中断就会返回
  30.                 res = -EINTR;
  31.                 break;
  32.             }
  33.         spin_unlock_irqrestore(&ep->lock,flags);
  34.         //进程进入睡眠状态直到规定的睡眠事件醒来或者注册的fd对应的poll驱动函数唤醒该            进程
  35.         jtimeout = schedule_timeout(jtimeout);
  36.         spin_lock_irqrestore(&ep->lock,flags);
  37.         }
  38.     //poll驱动唤醒了该进程,现在就将对应的poll从等待队列中清除出去,并设置为运行状态
  39.     __remove_wait_queue(&ep->wq,&wait);
  40.     set_current_state(TASK_RUNNING);
  41.     }
  42.     eavail = !list_empty(&ep->rdllist);
  43.     spin_unlock_irqrestore(&ep->lock,flags);
  44.     //没有被中断,有就绪事件,并且向用户空间发送成功,就返回
  45.     if(!res && eavail && !(res = ep_send_events(ep,events,maxevents))
  46.         &&jtimeout)
  47.         goto retry;

  48.     return res;
  49. }

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

  1. static int ep_send_events(struct eventpoll *ep,struct epoll_event __user *events,int maxevents)
  2. {
  3.     int eventcnt,error = -EFAULT,pwake = 0;
  4.     unsigned int revents;
  5.     unsigned long flags;
  6.     struct epitem *epi,*nepi;
  7.     struct list_head txlist;

  8.     INIT_LIST_HEAD(&txlist);
  9.     mutex_lock(&ep->mtx);

  10.     spin_lock_irqsave(&ep->lock,flags);
  11.     //将ep->rdllist链表加入到txlist链表中去,这样的话rdllist链表就为空了
  12.     list_splice(&ep->rdllist,&txlist);
  13.     INIT_LIST_HEAD(&ep->rdllist);
  14.     ep->ovflist = NULL;
  15.     spin_unlock_irqrestore(&ep->lock,flags);
  16.     //将rdllist链表中的每一项都发送至用户空间
  17.     for(eventcnt = 0; !list_empty(&txlist) && eventcnt < maxevents;) {
  18.         
  19.         epi = list_first_entry(&txlist,struct epitem,rdllink);
  20.         list_del_init(&epi->rdllink);    
  21.         //立刻返回当前文件的就绪事件
  22.         revents = epi->ffd.file->f_op->poll(epi->ffd.file,NULL);
  23.         revents &= epi->event.events;
  24.         
  25.         if(revents) {
  26.             //将就绪事件的poll_event发送至用户空间
  27.             if(__put_user(revents,&events[eventcnt.].events) ||
  28.              __put_user(epi->event.data,&events[eventcnt].data))
  29.                 
  30.                 goto errxit;
  31.             //#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)
  32.             if(epi->event.events & EPOLLONESHOT)
  33.                 epi->event.events &= EP_PRIVATE_BITS;
  34.             eventcnt++;
  35.         }
  36.      //非边缘触发,且事件就绪时,就将epi->rdllink加入到rdllist链表中,实际上就是将没有标记为ET模式的fd又放回到rdllist中,这样下次就绪时又能将其发送至用户空间了
  37.      if(!(epi->event.events & EPOLLET) && (revents & 
  38.                 epi->event.events))
  39.             list_add_tail(&epi->rdllink,&ep->rdllist);
  40. }
  41.     error = 0;
  42. errixt:
  43.     spin_lock_irqsave(&ep->lock,flags);
  44.     //在执行上面的代码期间,又有可能有就绪事件,这样的话就进入了ovflist队列,这样有需要再一次确认一次    
  45.     for(nepi = ep->ovflist;(epi = nepi)!= NULL;
  46.      nepi = epi->next;epi->next = EP_UNACTIVE_PTR) {
  47.         //链表为空且没有ET事件发生,#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET),这里也和上面的一样
  48.         if(!ep_is_linked(&epi->rdllink) && (epi->event.events & 
  49.             ~EP_PRIVATE_BITS))
  50.             //又将rdllink其加入到rdllist中
  51.                 list_add_tail(&epi->rdllink,&ep->rdllist);
  52.     }
  53.     //#define EP_UNACTIVE_PTR    ((void*) -1L)
  54.     ep->ovflist = EP_UNACTIVE_PTR;
  55.     list_spice(&txlist,&ep->rdllist);//现在又将txlist链表加入到rdllist链表中去
  56.     if(!list_empty(&ep->rdllist))
  57.     {
  58.         //等待的队列不为空
  59.         if(waitqueue_active(&ep->wq))
  60.             
  61.             __wake_up_locked(&ep->wq,TASK_UNINTERRUPTIBLE |
  62.             TASK_INTERRUPTIBLE);
  63.         //如果poll队列不为空,则唤醒的次数加1
  64.         if(waitqueue_active(&ep->poll_wait))
  65.             pwake++;
  66.     }
  67.     spin_unlock_irqrestore(&ep->lock,flags);
  68.     mutex_unlock(&ep->mtx);
  69.     if(pwake)
  70.         ep_poll_safewake(&psw,&ep->poll_wait);
  71.     return eventcnt == 0?error:eventcnt;
  72. }

这样epoll_wait的调用顺序为:

epoll源码分析(三)_第1张图片

参考资料:

linux-2.6.24.3源代码

http://donghao.org/2009/08/linuxiapolliepollaueouaeaeeio.html


你可能感兴趣的:(epoll源码分析(三))