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 }