epoll源码分析---sys_epoll_ctl()函数

一、sys_epoll_ctl()函数
 源码和注释如下:
  1. /* 
  2.  * @epfd: epool_create创建的用于eventpoll的fd 
  3.  * @op: 控制的命令类型 
  4.  * @fd: 要操作的文件描述符 
  5.  * @event:与fd相关的对象. 
  6.  */  
  7. SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,  
  8.         struct epoll_event __user *, event)  
  9. {  
  10.     int error;  
  11.     struct file *file, *tfile;  
  12.     struct eventpoll *ep;  
  13.     struct epitem *epi;  
  14.     struct epoll_event epds;  
  15.   
  16.     error = -EFAULT;  
  17.     /* 
  18.      * 检查是否需要从用户空间拷贝event参数,如果需要拷贝,则调用 
  19.      * copy_from_user来拷贝. 
  20.      */  
  21.     if (ep_op_has_event(op) &&  
  22.      copy_from_user(&epds, event, sizeof(struct epoll_event)))  
  23.         goto error_return;  
  24.   
  25.     /* Get the "struct file *" for the eventpoll file */  
  26.     error = -EBADF;  
  27.     /* 
  28.      * 获取epfd对应的file实例 
  29.      */  
  30.     file = fget(epfd);  
  31.     if (!file)  
  32.         goto error_return;  
  33.   
  34.     /* Get the "struct file *" for the target file */  
  35.     /* 
  36.      * 获取要操作的文件描述符对应的file实例 
  37.      */  
  38.     tfile = fget(fd);  
  39.     if (!tfile)  
  40.         goto error_fput;  
  41.   
  42.     /* The target file descriptor must support poll */  
  43.     /* 
  44.      * 检查fd对应的文件是否支持poll 
  45.      */  
  46.     error = -EPERM;  
  47.     if (!tfile->f_op || !tfile->f_op->poll)  
  48.         goto error_tgt_fput;  
  49.   
  50.     /* 
  51.      * We have to check that the file structure underneath the file descriptor 
  52.      * the user passed to us _is_ an eventpoll file. And also we do not permit 
  53.      * adding an epoll file descriptor inside itself. 
  54.      */  
  55.     error = -EINVAL;  
  56.     /* 
  57.      * 检查fd对应的文件是否是一个eventpoll文件 
  58.      */  
  59.     if (file == tfile || !is_file_epoll(file))  
  60.         goto error_tgt_fput;  
  61.   
  62.     /* 
  63.      * At this point it is safe to assume that the "private_data" contains 
  64.      * our own data structure. 
  65.      */  
  66.     /* 
  67.      * 获取eventpoll文件中的私有数据,该数据是在epoll_create中创建的。 
  68.      */  
  69.     ep = file->private_data;  
  70.   
  71.     mutex_lock(&ep->mtx);  
  72.   
  73.     /* 
  74.      * Try to lookup the file inside our RB tree, Since we grabbed "mtx" 
  75.      * above, we can be sure to be able to use the item looked up by 
  76.      * ep_find() till we release the mutex. 
  77.      */  
  78.      /* 
  79.       * 在eventpoll中存储文件描述符信息的红黑树中查找指定的fd对应的epitem实例 
  80.       */  
  81.     epi = ep_find(ep, tfile, fd);  
  82.   
  83.     error = -EINVAL;  
  84.     switch (op) {  
  85.     case EPOLL_CTL_ADD:  
  86.         /* 
  87.          * 如果要添加的fd不存在,则调用ep_insert()插入到红黑树中, 
  88.          * 如果已存在,则返回EEXIST错误. 
  89.          */  
  90.         if (!epi) {  
  91.             epds.events |= POLLERR | POLLHUP;  
  92.             error = ep_insert(ep, &epds, tfile, fd);  
  93.         } else  
  94.             error = -EEXIST;  
  95.         break;  
  96.     case EPOLL_CTL_DEL:  
  97.         if (epi)  
  98.             error = ep_remove(ep, epi);  
  99.         else  
  100.             error = -ENOENT;  
  101.         break;  
  102.     case EPOLL_CTL_MOD:  
  103.         if (epi) {  
  104.             epds.events |= POLLERR | POLLHUP;  
  105.             error = ep_modify(ep, epi, &epds);  
  106.         } else  
  107.             error = -ENOENT;  
  108.         break;  
  109.     }  
  110.     mutex_unlock(&ep->mtx);  
  111.   
  112. error_tgt_fput:  
  113.     fput(tfile);  
  114. error_fput:  
  115.     fput(file);  
  116. error_return:  
  117.   
  118.     return error;  
  119. }  


该函数首先在eventpoll中查找操作的fd对应的epitem对象是否存在,然后根据用户指定的命令参数,作相应的处理。每个添加到epoll的文件都会附加到一个epitem对象中。epoll的删除文件和修改文件命令,分别有ep_remove()和ep_modify()来完成,这两个函数比较简单,不作过多分析。主要关心的是epoll的添加命令对应的函数ep_insert().

二、ep_insert()函数
源码及分析如下:
  1. /* 
  2.  * Must be called with "mtx" held. 
  3.  */  
  4. static int ep_insert(struct eventpoll *ep, struct epoll_event *event,  
  5.          struct file *tfile, int fd)  
  6. {  
  7.     int error, revents, pwake = 0;  
  8.     unsigned long flags;  
  9.     struct epitem *epi;  
  10.     struct ep_pqueue epq;  
  11.   
  12.     /* 
  13.      * 检查epoll监视的文件描述符的个数是否超过max_user_watches, 
  14.      * max_user_watches用来存储每个用户使用epoll可以监视的文件 
  15.      * 描述符个数 
  16.      */  
  17.     if (unlikely(atomic_read(&ep->user->epoll_watches) >=  
  18.          max_user_watches))  
  19.         return -ENOSPC;  
  20.     /* 
  21.      * 每个加入到epoll中的文件都会附加到一个epitem实例中, 
  22.      * 分配当前文件对应的epitem实例。 
  23.      */  
  24.     if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))  
  25.         return -ENOMEM;  
  26.   
  27.     /* 
  28.      * 初始化新分配的epitem实例 
  29.      */  
  30.     INIT_LIST_HEAD(&epi->rdllink);  
  31.     INIT_LIST_HEAD(&epi->fllink);  
  32.     INIT_LIST_HEAD(&epi->pwqlist);  
  33.     epi->ep = ep;  
  34.     ep_set_ffd(&epi->ffd, tfile, fd);  
  35.     epi->event = *event;  
  36.     epi->nwait = 0;  
  37.     epi->next = EP_UNACTIVE_PTR;  
  38.   
  39.     /* Initialize the poll table using the queue callback */  
  40.     epq.epi = epi;  
  41.     init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);  
  42.   
  43.     /* 
  44.      * 如果fd是套接字,f_op为socket_file_ops,poll函数是 
  45.      * sock_poll()。如果是TCP套接字的话,进而会调用 
  46.      * 到tcp_poll()函数。此处调用poll函数查看当前 
  47.      * 文件描述符的状态,存储在revents中。 
  48.      * 在poll的处理函数(tcp_poll())中,会调用sock_poll_wait(), 
  49.      * 在sock_poll_wait()中会调用到epq.pt.qproc指向的函数, 
  50.      * 也就是ep_ptable_queue_proc()。 
  51.      */  
  52.     revents = tfile->f_op->poll(tfile, &epq.pt);  
  53.   
  54.     /* 
  55.      * ep_ptable_queue_proc()中如果分配内存失败时,会 
  56.      * 将nwait置为-1。 
  57.      */  
  58.     error = -ENOMEM;  
  59.     if (epi->nwait < 0)  
  60.         goto error_unregister;  
  61.   
  62.     /* Add the current item to the list of active epoll hook for this file */  
  63.     spin_lock(&tfile->f_lock);  
  64.     /* 
  65.      * 将当前的epitem加入tfile的f_ep_links链表中, 
  66.      * 在从epoll中移除文件时,用户清理文件对应的 
  67.      * epitem实例。 
  68.      */  
  69.     list_add_tail(&epi->fllink, &tfile->f_ep_links);  
  70.     spin_unlock(&tfile->f_lock);  
  71.   
  72.     /* 
  73.      * 将当前的epitem加入到存储监视的所有文件的红黑树中. 
  74.      */  
  75.     ep_rbtree_insert(ep, epi);  
  76.   
  77.     /* We have to drop the new item inside our item list to keep track of it */  
  78.     spin_lock_irqsave(&ep->lock, flags);  
  79.   
  80.     /* 
  81.      * 如果要监视的文件状态已经就绪并且还没有加入到就绪队列中,则将当前的 
  82.      * epitem加入到就绪队列中.如果有进程正在等待该文件的状态就绪,则 
  83.      * 唤醒一个等待的进程. 
  84.      */  
  85.     if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {  
  86.         list_add_tail(&epi->rdllink, &ep->rdllist);  
  87.   
  88.         /* Notify waiting tasks that events are available */  
  89.         /* 
  90.          * 如果有进程正在等待文件的状态就绪,也就是 
  91.          * 调用epoll_wait睡眠的进程正在等待,则唤醒一个 
  92.          * 等待进程。 
  93.          */  
  94.         if (waitqueue_active(&ep->wq))  
  95.             wake_up_locked(&ep->wq);  
  96.         /* 
  97.          * 如果有进程等待eventpoll文件本身的事件就绪, 
  98.          * 则增加临时变量pwake的值,pwake的值不为0时, 
  99.          * 在释放lock后,会唤醒等待进程。 
  100.          */  
  101.         if (waitqueue_active(&ep->poll_wait))  
  102.             pwake++;  
  103.     }  
  104.   
  105.     spin_unlock_irqrestore(&ep->lock, flags);  
  106.   
  107.     /* 
  108.      * 增加eventpoll监视的文件数量。 
  109.      */  
  110.     atomic_inc(&ep->user->epoll_watches);  
  111.   
  112.     /* We have to call this outside the lock */  
  113.     /* 
  114.      * 唤醒等待eventpoll文件状态就绪的进程 
  115.      */  
  116.      *   
  117.     if (pwake)  
  118.         ep_poll_safewake(&ep->poll_wait);  
  119.   
  120.     return 0;  
  121.   
  122. error_unregister:  
  123.     ep_unregister_pollwait(ep, epi);  
  124.   
  125.     /* 
  126.      * We need to do this because an event could have been arrived on some 
  127.      * allocated wait queue. Note that we don't care about the ep->ovflist 
  128.      * list, since that is used/cleaned only inside a section bound by "mtx". 
  129.      * And ep_insert() is called with "mtx" held. 
  130.      */  
  131.     spin_lock_irqsave(&ep->lock, flags);  
  132.     if (ep_is_linked(&epi->rdllink))  
  133.         list_del_init(&epi->rdllink);  
  134.     spin_unlock_irqrestore(&ep->lock, flags);  
  135.   
  136.     kmem_cache_free(epi_cache, epi);  
  137.   
  138.     return error;  
  139. }  
ep_insert()函数首先分配fd要附加到的epitem实例,初始化后会添加到eventpoll中存储文件的红黑树、监视文件的 f_ep_links链表中以及监视文件的唤醒队列中。在加入到监视文件的唤醒队列时,如果用户关心的事件发生时,会将epitem实例添加到eventpoll的就绪队列中。第52行代码就是将epitem实例添加到文件的唤醒队列中,真正添加的操作是 ep_ptable_queue_proc()函数。

三、ep_ptable_queue_proc()函数
源码及注释如下:
  1. /* 
  2.  * 在文件操作中的poll函数中调用,将epoll的回调函数 
  3.  * 加入到目标文件的唤醒队列中。 
  4.  * 如果监视的文件是套接字,参数whead则是sock结构的sk_sleep 
  5.  * 成员的地址 
  6.  */  
  7. static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,  
  8.                  poll_table *pt)  
  9. {  
  10.     struct epitem *epi = ep_item_from_epqueue(pt);  
  11.     struct eppoll_entry *pwq;  
  12.   
  13.     if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {  
  14.         init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);  
  15.         pwq->whead = whead;  
  16.         pwq->base = epi;  
  17.         add_wait_queue(whead, &pwq->wait);  
  18.         list_add_tail(&pwq->llink, &epi->pwqlist);  
  19.         epi->nwait++;  
  20.     } else {  
  21.         /* We have to signal that an error occurred */  
  22.         /* 
  23.          * 如果分配内存失败,则将nwait置为-1,表示 
  24.          * 发生错误,即内存分配失败,或者已发生错误 
  25.          */  
  26.         epi->nwait = -1;  
  27.     }  
  28. }  
从上面的函数可以看出,注册在监视文件的唤醒队列上的回调方法是 ep_poll_callback()函数。也就是当有事件发生时,会唤醒监视文件上等待的进程。在tcp_prequeue()函数中当有数据达到时唤醒等待队列sk_sleep上的进程,代码片段如下:
  1. static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)  
  2. {  
  3.     .......  
  4.         wake_up_interruptible_poll(sk->sk_sleep,  
  5.                      POLLIN | POLLRDNORM | POLLRDBAND);  
  6.     .......  
  7. }  
wake_up_interruptible_poll()函数会调用注册到sk_sleep中的回调函数,如果是eventpoll注册的话,该回调函数就是 ep_poll_callback()。

四、 ep_poll_callback()函数
  1. /* 
  2.   * 如果文件类型支持epoll并且有事件发生,发生的事件通过 
  3.   * 参数key来传送,参见tcp_prequeue()函数中对wake_up_interruptible_poll() 
  4.   * 的调用。 
  5.   * @wait: 调用ep_ptable_queue_proc()加入到文件中的唤醒队列时分配的 
  6.   * eppoll_entry实例的wait成员的地址 
  7.   * @mode:该参数在回调函数ep_poll_callback()中没有使用,其值为进程 
  8.   * 睡眠时的状态 
  9.   * @sync: 唤醒等待进程的标志 
  10.   */  
  11. static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *key)  
  12. {  
  13.     int pwake = 0;  
  14.     unsigned long flags;  
  15.     struct epitem *epi = ep_item_from_wait(wait);  
  16.     struct eventpoll *ep = epi->ep;  
  17.   
  18.     spin_lock_irqsave(&ep->lock, flags);  
  19.   
  20.     /* 
  21.      * If the event mask does not contain any poll(2) event, we consider the 
  22.      * descriptor to be disabled. This condition is likely the effect of the 
  23.      * EPOLLONESHOT bit that disables the descriptor when an event is received, 
  24.      * until the next EPOLL_CTL_MOD will be issued. 
  25.      */  
  26.     /* 
  27.      * epi->event.events中存储的是用户空间关心的事件,如果该成员 
  28.      * 没有包含任何poll事件,则跳转到out_unlock处处理 
  29.      */  
  30.     if (!(epi->event.events & ~EP_PRIVATE_BITS))  
  31.         goto out_unlock;  
  32.   
  33.     /* 
  34.      * Check the events coming with the callback. At this stage, not 
  35.      * every device reports the events in the "key" parameter of the 
  36.      * callback. We need to be able to handle both cases here, hence the 
  37.      * test for "key" != NULL before the event match test. 
  38.      */  
  39.     /* 
  40.      * 如果key不为NULL,也就是值不是0,但是用户关心的 
  41.      * 事件并没有发生,则跳转到out_unlock处处理。参数key 
  42.      * 应该不会为0 
  43.      */  
  44.     if (key && !((unsigned long) key & epi->event.events))  
  45.         goto out_unlock;  
  46.   
  47.     /* 
  48.      * If we are trasfering events to userspace, we can hold no locks 
  49.      * (because we're accessing user memory, and because of linux f_op->poll() 
  50.      * semantics). All the events that happens during that period of time are 
  51.      * chained in ep->ovflist and requeued later on. 
  52.      */  
  53.     /*  
  54.      * ep_scan_ready_list()是向用户空间传递事件的处理函数, 
  55.      * ep_scan_ready_list()函数执行时会将ovflist链表中的元素 
  56.      * 暂存到一个临时变量中,然后将ovflist成员置为NULL, 
  57.      * 而EP_UNACTIVE_PTR的定义如下: 
  58.      * #define EP_UNACTIVE_PTR ((void *) -1L) 
  59.      * 因此(ep->ovflist != EP_UNACTIVE_PTR)成立时,正在向用户空间 
  60.      * 传递事件。 
  61.      * 如果当前正在向用户空间传递事件,则将 
  62.      * 当前的事件对应的epitem实例加入到ovflist链表中。 
  63.      */  
  64.     if (unlikely(ep->ovflist != EP_UNACTIVE_PTR)) {  
  65.         /* 
  66.          * 如果epi->next不等于EP_UNACTIVE_PTR,则说明已经 
  67.          * 添加到ovflist链表中,就不用再添加了 
  68.          */  
  69.         if (epi->next == EP_UNACTIVE_PTR) {  
  70.             epi->next = ep->ovflist;  
  71.             ep->ovflist = epi;  
  72.         }  
  73.         goto out_unlock;  
  74.     }  
  75.   
  76.     /* If this file is already in the ready list we exit soon */  
  77.     /* 
  78.      * 如果当前没有在向用户空间传递事件,用户 
  79.      * 关心的事件已经发生,并且还没有加入到就绪 
  80.      * 队列中,则将当前的epitem实例加入到就绪队列中。 
  81.      */  
  82.     if (!ep_is_linked(&epi->rdllink))  
  83.         list_add_tail(&epi->rdllink, &ep->rdllist);  
  84.   
  85.     /* 
  86.      * Wake up ( if active ) both the eventpoll wait list and the ->poll() 
  87.      * wait list. 
  88.      */  
  89.     /* 
  90.      * 唤醒调用epoll_wait()函数时睡眠的进程。 
  91.      */  
  92.     if (waitqueue_active(&ep->wq))  
  93.         wake_up_locked(&ep->wq);  
  94.     /* 
  95.      * 唤醒等待eventpoll文件状态就绪的进程 
  96.      */  
  97.     if (waitqueue_active(&ep->poll_wait))  
  98.         pwake++;  
  99.   
  100. out_unlock:  
  101.     spin_unlock_irqrestore(&ep->lock, flags);  
  102.   
  103.     /* We have to call this outside the lock */  
  104.     /* 
  105.      * 唤醒等待eventpoll文件的状态就绪的进程 
  106.      */  
  107.     if (pwake)  
  108.         ep_poll_safewake(&ep->poll_wait);  
  109.   
  110.     return 1;  
  111. }  
该函数主要的功能是将被监视文件的等待事件就绪时,将文件对应的epitem实例添加到就绪队列中,当用户调用epoll_wait()时,内核会将就绪队列中的事件报告给用户

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值