zoukankan      html  css  js  c++  java
  • epoll源码深度剖析

    1.基本数据结构

    分别是 eventpoll、epitem 和 eppoll_entry。

    1.1 eventpoll

    我们先看一下 eventpoll 这个数据结构,这个数据结构是我们在调用 epoll_create 之后内核侧创建的一个句柄,表示了一个 epoll 实例。后续如果我们再调用 epoll_ctl 和 epoll_wait 等,都是对这个 eventpoll 数据进行操作,这部分数据会被保存在 epoll_create 创建的匿名文件 file 的 private_data 字段中

     1 /*
     2  * This structure is stored inside the "private_data" member of the file
     3  * structure and represents the main data structure for the eventpoll
     4  * interface.
     5  */
     6 struct eventpoll {
     7     /* Protect the access to this structure */
     8     spinlock_t lock;
     9 
    10     /*
    11      * This mutex is used to ensure that files are not removed
    12      * while epoll is using them. This is held during the event
    13      * collection loop, the file cleanup path, the epoll file exit
    14      * code and the ctl operations.
    15      */
    16     struct mutex mtx;
    17 
    18     /* Wait queue used by sys_epoll_wait() */
    19     //这个队列里存放的是执行epoll_wait从而等待的进程队列
    20     wait_queue_head_t wq;
    21 
    22     /* Wait queue used by file->poll() */
    23     //这个队列里存放的是该eventloop作为poll对象的一个实例,加入到等待的队列
    24     //这是因为eventpoll本身也是一个file, 所以也会有poll操作
    25     wait_queue_head_t poll_wait;
    26 
    27     /* List of ready file descriptors */
    28     //这里存放的是事件就绪的fd列表,链表的每个元素是下面的epitem
    29     struct list_head rdllist;
    30 
    31     /* RB tree root used to store monitored fd structs */
    32     //这是用来快速查找fd的红黑树
    33     struct rb_root_cached rbr;
    34 
    35     /*
    36      * This is a single linked list that chains all the "struct epitem" that
    37      * happened while transferring ready events to userspace w/out
    38      * holding ->lock.
    39      */
    40     struct epitem *ovflist;
    41 
    42     /* wakeup_source used when ep_scan_ready_list is running */
    43     struct wakeup_source *ws;
    44 
    45     /* The user that created the eventpoll descriptor */
    46     struct user_struct *user;
    47 
    48     //这是eventloop对应的匿名文件,充分体现了Linux下一切皆文件的思想
    49     struct file *file;
    50 
    51     /* used to optimize loop detection check */
    52     int visited;
    53     struct list_head visited_list_link;
    54 
    55 #ifdef CONFIG_NET_RX_BUSY_POLL
    56     /* used to track busy poll napi_id */
    57     unsigned int napi_id;
    58 #endif
    59 };

    这个 epitem 结构是干什么用的呢?

    每当我们调用 epoll_ctl 增加一个 fd 时,内核就会为我们创建出一个 epitem 实例,并且把这个实例作为红黑树的一个子节点,增加到 eventpoll 结构体中的红黑树中,对应的字段是 rbr。这之后,查找每一个 fd 上是否有事件发生都是通过红黑树上的 epitem 来操作。

     1 /*
     2  * Each file descriptor added to the eventpoll interface will
     3  * have an entry of this type linked to the "rbr" RB tree.
     4  * Avoid increasing the size of this struct, there can be many thousands
     5  * of these on a server and we do not want this to take another cache line.
     6  */
     7 struct epitem {
     8     union {
     9         /* RB tree node links this structure to the eventpoll RB tree */
    10         struct rb_node rbn;
    11         /* Used to free the struct epitem */
    12         struct rcu_head rcu;
    13     };
    14 
    15     /* List header used to link this structure to the eventpoll ready list */
    16     //将这个epitem连接到eventpoll 里面的rdllist的list指针
    17     struct list_head rdllink;
    18 
    19     /*
    20      * Works together "struct eventpoll"->ovflist in keeping the
    21      * single linked chain of items.
    22      */
    23     struct epitem *next;
    24 
    25     /* The file descriptor information this item refers to */
    26     //epoll监听的fd
    27     struct epoll_filefd ffd;
    28 
    29     /* Number of active wait queue attached to poll operations */
    30     //一个文件可以被多个epoll实例所监听,这里就记录了当前文件被监听的次数
    31     int nwait;
    32 
    33     /* List containing poll wait queues */
    34     struct list_head pwqlist;
    35 
    36     /* The "container" of this item */
    37     //当前epollitem所属的eventpoll
    38     struct eventpoll *ep;
    39 
    40     /* List header used to link this item to the "struct file" items list */
    41     struct list_head fllink;
    42 
    43     /* wakeup_source used when EPOLLWAKEUP is set */
    44     struct wakeup_source __rcu *ws;
    45 
    46     /* The structure that describe the interested events and the source fd */
    47     struct epoll_event event;
    48 };

    每次当一个 fd 关联到一个 epoll 实例,就会有一个 eppoll_entry 产生。eppoll_entry 的结构如下:

     1 /* Wait structure used by the poll hooks */
     2 struct eppoll_entry {
     3     /* List header used to link this structure to the "struct epitem" */
     4     struct list_head llink;
     5 
     6     /* The "base" pointer is set to the container "struct epitem" */
     7     struct epitem *base;
     8 
     9     /*
    10      * Wait queue item that will be linked to the target file wait
    11      * queue head.
    12      */
    13     wait_queue_entry_t wait;
    14 
    15     /* The wait queue head that linked the "wait" wait queue item */
    16     wait_queue_head_t *whead;
    17 };

    epoll_create:

    我们在使用 epoll 的时候,首先会调用 epoll_create 来创建一个 epoll 实例。这个函数是如何工作的呢?

    首先,epoll_create 会对传入的 flags 参数做简单的验证。

    1 /* Check the EPOLL_* constant for consistency.  */
    2 BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
    3 
    4 if (flags & ~EPOLL_CLOEXEC)
    5     return -EINVAL;
    6 /*

    接下来,内核申请分配 eventpoll 需要的内存空间

    1 /* Create the internal data structure ("struct eventpoll").
    2 */
    3 error = ep_alloc(&ep);
    4 if (error < 0)
    5   return error;

    在接下来,epoll_create 为 epoll 实例分配了匿名文件和文件描述字,其中 fd 是文件描述字,file 是一个匿名文件。这里充分体现了 UNIX 下一切都是文件的思想。

    注意,eventpoll 的实例会保存一份匿名文件的引用,通过调用 fd_install 函数将匿名文件和文件描述字完成了绑定。

    这里还有一个特别需要注意的地方,在调用 anon_inode_get_file 的时候,epoll_create 将 eventpoll 作为匿名文件 file 的 private_data 保存了起来,这样,在之后通过 epoll 实例的文件描述字来查找时,就可以快速地定位到 eventpoll 对象了。最后,这个文件描述字作为 epoll 的文件句柄,被返回给 epoll_create 的调用者。

     1 /*
     2  * Creates all the items needed to setup an eventpoll file. That is,
     3  * a file structure and a free file descriptor.
     4  */
     5 fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
     6 if (fd < 0) {
     7     error = fd;
     8     goto out_free_ep;
     9 }
    10 file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
    11              O_RDWR | (flags & O_CLOEXEC));
    12 if (IS_ERR(file)) {
    13     error = PTR_ERR(file);
    14     goto out_free_fd;
    15 }
    16 ep->file = file;
    17 fd_install(fd, file);
    18 return fd;

    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 }

    查找 epoll 实例首先,epoll_ctl 函数通过 epoll 实例句柄来获得对应的匿名文件,这一点很好理解,UNIX 下一切都是文件,epoll 的实例也是一个匿名文件。

    1 //获得epoll实例对应的匿名文件
    2 f = fdget(epfd);
    3 if (!f.file)
    4     goto error_return;

    接下来,获得添加的套接字对应的文件,这里 tf 表示的是 target file,即待处理的目标文件。

    1 /* Get the "struct file *" for the target file */
    2 //获得真正的文件,如监听套接字、读写套接字
    3 tf = fdget(fd);
    4 if (!tf.file)
    5     goto error_fput;

    再接下来,进行了一系列的数据验证,以保证用户传入的参数是合法的,比如 epfd 真的是一个 epoll 实例句柄,而不是一个普通文件描述符。

    1 /* The target file descriptor must support poll */
    2 //如果不支持poll,那么该文件描述字是无效的
    3 error = -EPERM;
    4 if (!tf.file->f_op->poll)
    5     goto error_tgt_fput;
    6 ...

    如果获得了一个真正的 epoll 实例句柄,就可以通过 private_data 获取之前创建的 eventpoll 实例了。

    1 /*
    2  * At this point it is safe to assume that the "private_data" contains
    3  * our own data structure.
    4  */
    5 ep = f.file->private_data;

    红黑树查找接下来 epoll_ctl 通过目标文件和对应描述字,在红黑树中查找是否存在该套接字,这也是 epoll 为什么高效的地方。红黑树(RB-tree)是一种常见的数据结构,这里 eventpoll 通过红黑树跟踪了当前监听的所有文件描述字,而这棵树的根就保存在 eventpoll 数据结构中

    1 /* RB tree root used to store monitored fd structs */
    2 struct rb_root_cached rbr;

    对于每个被监听的文件描述字,都有一个对应的 epitem 与之对应,epitem 作为红黑树中的节点就保存在红黑树中。

    1 /*
    2  * Try to lookup the file inside our RB tree, Since we grabbed "mtx"
    3  * above, we can be sure to be able to use the item looked up by
    4  * ep_find() till we release the mutex.
    5  */
    6 epi = ep_find(ep, tf.file, fd);

    红黑树是一棵二叉树,作为二叉树上的节点,epitem 必须提供比较能力,以便可以按大小顺序构建出一棵有序的二叉树。其排序能力是依靠 epoll_filefd 结构体来完成的,epoll_filefd 可以简单理解为需要监听的文件描述字,它对应到二叉树上的节点

    可以看到这个还是比较好理解的,按照文件的地址大小排序。如果两个相同,就按照文件文件描述字来排序。

     1 struct epoll_filefd {
     2   struct file *file; // pointer to the target file struct corresponding to the fd
     3   int fd; // target file descriptor number
     4 } __packed;
     5 
     6 /* Compare RB tree keys */
     7 static inline int ep_cmp_ffd(struct epoll_filefd *p1,
     8                             struct epoll_filefd *p2)
     9 {
    10   return (p1->file > p2->file ? +1:
    11        (p1->file < p2->file ? -1 : p1->fd - p2->fd));
    12 }

    在进行完红黑树查找之后,如果发现是一个 ADD 操作,并且在树中没有找到对应的二叉树节点,就会调用 ep_insert 进行二叉树节点的增加。

    1 case EPOLL_CTL_ADD:
    2     if (!epi) {
    3         epds.events |= POLLERR | POLLHUP;
    4         error = ep_insert(ep, &epds, tf.file, fd, full_check);
    5     } else
    6         error = -EEXIST;
    7     if (full_check)
    8         clear_tfile_check_list();
    9     break;

    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 首先判断当前监控的文件值是否超过了 /proc/sys/fs/epoll/max_user_watches 的预设最大值,如果超过了则直接返回错误。

    1 user_watches = atomic_long_read(&ep->user->epoll_watches);
    2 if (unlikely(user_watches >= max_user_watches))
    3     return -ENOSPC;

    接下来是分配资源和初始化动作。

     1 if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
     2         return -ENOMEM;
     3 
     4     /* Item initialization follow here ... */
     5     INIT_LIST_HEAD(&epi->rdllink);
     6     INIT_LIST_HEAD(&epi->fllink);
     7     INIT_LIST_HEAD(&epi->pwqlist);
     8     epi->ep = ep;
     9     ep_set_ffd(&epi->ffd, tfile, fd);
    10     epi->event = *event;
    11     epi->nwait = 0;
    12     epi->next = EP_UNACTIVE_PTR;

    再接下来的事情非常重要,ep_insert 会为加入的每个文件描述字设置回调函数。这个回调函数是通过函数 ep_ptable_queue_proc 来进行设置的。这个回调函数是干什么的呢?其实,对应的文件描述字上如果有事件发生,就会调用这个函数,比如套接字缓冲区有数据了,就会回调这个函数。这个函数就是 ep_poll_callback。这里你会发现,原来内核设计也是充满了事件回调的原理。

     1 /*
     2  * This is the callback that is used to add our wait queue to the
     3  * target file wakeup lists.
     4  */
     5 static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,poll_table *pt)
     6 {
     7     struct epitem *epi = ep_item_from_epqueue(pt);
     8     struct eppoll_entry *pwq;
     9 
    10     if (epi>nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
    11         init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
    12         pwq->whead = whead;
    13         pwq->base = epi;
    14         if (epi->event.events & EPOLLEXCLUSIVE)
    15             add_wait_queue_exclusive(whead, &pwq->wait);
    16         else
    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         epi->nwait = -1;
    23     }
    24 }

    ep_poll_callback

    ep_poll_callback 函数的作用非常重要,它将内核事件真正地和 epoll 对象联系了起来。它又是怎么实现的呢?首先,通过这个文件的 wait_queue_entry_t 对象找到对应的 epitem 对象,因为 eppoll_entry 对象里保存了 wait_quue_entry_t,根据 wait_quue_entry_t 这个对象的地址就可以简单计算出 eppoll_entry 对象的地址,从而可以获得 epitem 对象的地址。这部分工作在 ep_item_from_wait 函数中完成。一旦获得 epitem 对象,就可以寻迹找到 eventpoll 实例。

      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 
    112 }
  • 相关阅读:
    Web服务器讲解与JavaWeb应用部署(本机,以Tomcat为例)
    DNS与DNS劫持原理、IP、域名、服务器访问浅讲
    MyArrayList——自己实现ArrayList
    Map排序——按key排序,按value排序
    储存对象并按对象某属性排序的几种方法
    常用集合类使用方法
    避免窗口切换闪烁——卡片式布局的使用方法
    [Netbeans]为面板设置背景图片
    JDBC编程之预编译SQL与防注入式攻击以及PreparedStatement的使用教程
    JDBC编程之事务的使用教程
  • 原文地址:https://www.cnblogs.com/mysky007/p/12284842.html
Copyright © 2011-2022 走看看