Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6276623
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类:

2013-01-15 02:17:38

一、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.     error = -EFAULT;
  16.     /*
  17.      * 检查是否需要从用户空间拷贝event参数,如果需要拷贝,则调用
  18.      * copy_from_user来拷贝.
  19.      */
  20.     if (ep_op_has_event(op) &&
  21.      copy_from_user(&epds, event, sizeof(struct epoll_event)))
  22.         goto error_return;

  23.     /* Get the "struct file *" for the eventpoll file */
  24.     error = -EBADF;
  25.     /*
  26.      * 获取epfd对应的file实例
  27.      */
  28.     file = fget(epfd);
  29.     if (!file)
  30.         goto error_return;

  31.     /* Get the "struct file *" for the target file */
  32.     /*
  33.      * 获取要操作的文件描述符对应的file实例
  34.      */
  35.     tfile = fget(fd);
  36.     if (!tfile)
  37.         goto error_fput;

  38.     /* The target file descriptor must support poll */
  39.     /*
  40.      * 检查fd对应的文件是否支持poll
  41.      */
  42.     error = -EPERM;
  43.     if (!tfile->f_op || !tfile->f_op->poll)
  44.         goto error_tgt_fput;

  45.     /*
  46.      * We have to check that the file structure underneath the file descriptor
  47.      * the user passed to us _is_ an eventpoll file. And also we do not permit
  48.      * adding an epoll file descriptor inside itself.
  49.      */
  50.     error = -EINVAL;
  51.     /*
  52.      * 检查fd对应的文件是否是一个eventpoll文件
  53.      */
  54.     if (file == tfile || !is_file_epoll(file))
  55.         goto error_tgt_fput;

  56.     /*
  57.      * At this point it is safe to assume that the "private_data" contains
  58.      * our own data structure.
  59.      */
  60.     /*
  61.      * 获取eventpoll文件中的私有数据,该数据是在epoll_create中创建的。
  62.      */
  63.     ep = file->private_data;

  64.     mutex_lock(&ep->mtx);

  65.     /*
  66.      * Try to lookup the file inside our RB tree, Since we grabbed "mtx"
  67.      * above, we can be sure to be able to use the item looked up by
  68.      * ep_find() till we release the mutex.
  69.      */
  70.      /*
  71.       * 在eventpoll中存储文件描述符信息的红黑树中查找指定的fd对应的epitem实例
  72.       */
  73.     epi = ep_find(ep, tfile, fd);

  74.     error = -EINVAL;
  75.     switch (op) {
  76.     case EPOLL_CTL_ADD:
  77.         /*
  78.          * 如果要添加的fd不存在,则调用ep_insert()插入到红黑树中,
  79.          * 如果已存在,则返回EEXIST错误.
  80.          */
  81.         if (!epi) {
  82.             epds.events |= POLLERR | POLLHUP;
  83.             error = ep_insert(ep, &epds, tfile, fd);
  84.         } else
  85.             error = -EEXIST;
  86.         break;
  87.     case EPOLL_CTL_DEL:
  88.         if (epi)
  89.             error = ep_remove(ep, epi);
  90.         else
  91.             error = -ENOENT;
  92.         break;
  93.     case EPOLL_CTL_MOD:
  94.         if (epi) {
  95.             epds.events |= POLLERR | POLLHUP;
  96.             error = ep_modify(ep, epi, &epds);
  97.         } else
  98.             error = -ENOENT;
  99.         break;
  100.     }
  101.     mutex_unlock(&ep->mtx);

  102. error_tgt_fput:
  103.     fput(tfile);
  104. error_fput:
  105.     fput(file);
  106. error_return:

  107.     return error;
  108. }
该函数首先在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.      * 检查epoll监视的文件描述符的个数是否超过max_user_watches,
  13.      * max_user_watches用来存储每个用户使用epoll可以监视的文件
  14.      * 描述符个数
  15.      */
  16.     if (unlikely(atomic_read(&ep->user->epoll_watches) >=
  17.          max_user_watches))
  18.         return -ENOSPC;
  19.     /*
  20.      * 每个加入到epoll中的文件都会附加到一个epitem实例中,
  21.      * 分配当前文件对应的epitem实例。
  22.      */
  23.     if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
  24.         return -ENOMEM;

  25.     /*
  26.      * 初始化新分配的epitem实例
  27.      */
  28.     INIT_LIST_HEAD(&epi->rdllink);
  29.     INIT_LIST_HEAD(&epi->fllink);
  30.     INIT_LIST_HEAD(&epi->pwqlist);
  31.     epi->ep = ep;
  32.     ep_set_ffd(&epi->ffd, tfile, fd);
  33.     epi->event = *event;
  34.     epi->nwait = 0;
  35.     epi->next = EP_UNACTIVE_PTR;

  36.     /* Initialize the poll table using the queue callback */
  37.     epq.epi = epi;
  38.     init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);

  39.     /*
  40.      * 如果fd是套接字,f_op为socket_file_ops,poll函数是
  41.      * sock_poll()。如果是TCP套接字的话,进而会调用
  42.      * 到tcp_poll()函数。此处调用poll函数查看当前
  43.      * 文件描述符的状态,存储在revents中。
  44.      * 在poll的处理函数(tcp_poll())中,会调用sock_poll_wait()
  45.      * 在sock_poll_wait()中会调用到epq.pt.qproc指向的函数,
  46.      * 也就是ep_ptable_queue_proc()
  47.      */
  48.     revents = tfile->f_op->poll(tfile, &epq.pt);

  49.     /*
  50.      * ep_ptable_queue_proc()中如果分配内存失败时,会
  51.      * 将nwait置为-1。
  52.      */
  53.     error = -ENOMEM;
  54.     if (epi->nwait < 0)
  55.         goto error_unregister;

  56.     /* Add the current item to the list of active epoll hook for this file */
  57.     spin_lock(&tfile->f_lock);
  58.     /*
  59.      * 将当前的epitem加入tfile的f_ep_links链表中,
  60.      * 在从epoll中移除文件时,用户清理文件对应的
  61.      * epitem实例。
  62.      */
  63.     list_add_tail(&epi->fllink, &tfile->f_ep_links);
  64.     spin_unlock(&tfile->f_lock);

  65.     /*
  66.      * 将当前的epitem加入到存储监视的所有文件的红黑树中.
  67.      */
  68.     ep_rbtree_insert(ep, epi);

  69.     /* We have to drop the new item inside our item list to keep track of it */
  70.     spin_lock_irqsave(&ep->lock, flags);

  71.     /*
  72.      * 如果要监视的文件状态已经就绪并且还没有加入到就绪队列中,则将当前的
  73.      * epitem加入到就绪队列中.如果有进程正在等待该文件的状态就绪,
  74.      * 唤醒一个等待的进程.
  75.      */
  76.     if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
  77.         list_add_tail(&epi->rdllink, &ep->rdllist);

  78.         /* Notify waiting tasks that events are available */
  79.         /*
  80.          * 如果有进程正在等待文件的状态就绪,也就是
  81.          * 调用epoll_wait睡眠的进程正在等待,则唤醒一个
  82.          * 等待进程。
  83.          */
  84.         if (waitqueue_active(&ep->wq))
  85.             wake_up_locked(&ep->wq);
  86.         /*
  87.          * 如果有进程等待eventpoll文件本身的事件就绪,
  88.          * 则增加临时变量pwake的值,pwake的值不为0时,
  89.          * 在释放lock后,会唤醒等待进程。
  90.          */
  91.         if (waitqueue_active(&ep->poll_wait))
  92.             pwake++;
  93.     }

  94.     spin_unlock_irqrestore(&ep->lock, flags);

  95.     /*
  96.      * 增加eventpoll监视的文件数量。
  97.      */
  98.     atomic_inc(&ep->user->epoll_watches);

  99.     /* We have to call this outside the lock */
  100.     /*
  101.      * 唤醒等待eventpoll文件状态就绪的进程
  102.      */
  103.      *
  104.     if (pwake)
  105.         ep_poll_safewake(&ep->poll_wait);

  106.     return 0;

  107. error_unregister:
  108.     ep_unregister_pollwait(ep, epi);

  109.     /*
  110.      * We need to do this because an event could have been arrived on some
  111.      * allocated wait queue. Note that we don't care about the ep->ovflist
  112.      * list, since that is used/cleaned only inside a section bound by "mtx".
  113.      * And ep_insert() is called with "mtx" held.
  114.      */
  115.     spin_lock_irqsave(&ep->lock, flags);
  116.     if (ep_is_linked(&epi->rdllink))
  117.         list_del_init(&epi->rdllink);
  118.     spin_unlock_irqrestore(&ep->lock, flags);

  119.     kmem_cache_free(epi_cache, epi);

  120.     return error;
  121. }
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.     if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
  13.         init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
  14.         pwq->whead = whead;
  15.         pwq->base = epi;
  16.         add_wait_queue(whead, &pwq->wait);
  17.         list_add_tail(&pwq->llink, &epi->pwqlist);
  18.         epi->nwait++;
  19.     } else {
  20.         /* We have to signal that an error occurred */
  21.         /*
  22.          * 如果分配内存失败,则将nwait置为-1,表示
  23.          * 发生错误,即内存分配失败,或者已发生错误
  24.          */
  25.         epi->nwait = -1;
  26.     }
  27. }
从上面的函数可以看出,注册在监视文件的唤醒队列上的回调方法是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.     spin_lock_irqsave(&ep->lock, flags);

  18.     /*
  19.      * If the event mask does not contain any poll(2) event, we consider the
  20.      * descriptor to be disabled. This condition is likely the effect of the
  21.      * EPOLLONESHOT bit that disables the descriptor when an event is received,
  22.      * until the next EPOLL_CTL_MOD will be issued.
  23.      */
  24.     /*
  25.      * epi->event.events中存储的是用户空间关心的事件,如果该成员
  26.      * 没有包含任何poll事件,则跳转到out_unlock处处理
  27.      */
  28.     if (!(epi->event.events & ~EP_PRIVATE_BITS))
  29.         goto out_unlock;

  30.     /*
  31.      * Check the events coming with the callback. At this stage, not
  32.      * every device reports the events in the "key" parameter of the
  33.      * callback. We need to be able to handle both cases here, hence the
  34.      * test for "key" != NULL before the event match test.
  35.      */
  36.     /*
  37.      * 如果key不为NULL,也就是值不是0,但是用户关心的
  38.      * 事件并没有发生,则跳转到out_unlock处处理。参数key
  39.      * 应该不会为0
  40.      */
  41.     if (key && !((unsigned long) key & epi->event.events))
  42.         goto out_unlock;

  43.     /*
  44.      * If we are trasfering events to userspace, we can hold no locks
  45.      * (because we're accessing user memory, and because of linux f_op->poll()
  46.      * semantics). All the events that happens during that period of time are
  47.      * chained in ep->ovflist and requeued later on.
  48.      */
  49.     /*
  50.      * ep_scan_ready_list()是向用户空间传递事件的处理函数,
  51.      * ep_scan_ready_list()函数执行时会将ovflist链表中的元素
  52.      * 暂存到一个临时变量中,然后将ovflist成员置为NULL,
  53.      * 而EP_UNACTIVE_PTR的定义如下:
  54.      * #define EP_UNACTIVE_PTR ((void *) -1L)
  55.      * 因此(ep->ovflist != EP_UNACTIVE_PTR)成立时,正在向用户空间
  56.      * 传递事件。
  57.      * 如果当前正在向用户空间传递事件,则将
  58.      * 当前的事件对应的epitem实例加入到ovflist链表中。
  59.      */
  60.     if (unlikely(ep->ovflist != EP_UNACTIVE_PTR)) {
  61.         /*
  62.          * 如果epi->next不等于EP_UNACTIVE_PTR,则说明已经
  63.          * 添加到ovflist链表中,就不用再添加了
  64.          */
  65.         if (epi->next == EP_UNACTIVE_PTR) {
  66.             epi->next = ep->ovflist;
  67.             ep->ovflist = epi;
  68.         }
  69.         goto out_unlock;
  70.     }

  71.     /* If this file is already in the ready list we exit soon */
  72.     /*
  73.      * 如果当前没有在向用户空间传递事件,用户
  74.      * 关心的事件已经发生,并且还没有加入到就绪
  75.      * 队列中,则将当前的epitem实例加入到就绪队列中。
  76.      */
  77.     if (!ep_is_linked(&epi->rdllink))
  78.         list_add_tail(&epi->rdllink, &ep->rdllist);

  79.     /*
  80.      * Wake up ( if active ) both the eventpoll wait list and the ->poll()
  81.      * wait list.
  82.      */
  83.     /*
  84.      * 唤醒调用epoll_wait()函数时睡眠的进程。
  85.      */
  86.     if (waitqueue_active(&ep->wq))
  87.         wake_up_locked(&ep->wq);
  88.     /*
  89.      * 唤醒等待eventpoll文件状态就绪的进程
  90.      */
  91.     if (waitqueue_active(&ep->poll_wait))
  92.         pwake++;

  93. out_unlock:
  94.     spin_unlock_irqrestore(&ep->lock, flags);

  95.     /* We have to call this outside the lock */
  96.     /*
  97.      * 唤醒等待eventpoll文件的状态就绪的进程
  98.      */
  99.     if (pwake)
  100.         ep_poll_safewake(&ep->poll_wait);

  101.     return 1;
  102. }
该函数主要的功能是将被监视文件的等待事件就绪时,将文件对应的epitem实例添加到就绪队列中,当用户调用epoll_wait()时,内核会将就绪队列中的事件报告给用户

阅读(702) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~