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 }

友情链接:
Copyright © 2022 86年世界杯_世界杯预选赛阿根廷 - fjyfzz.com All Rights Reserved.