网络编程实战
盛延敏
前大众点评云平台首席架构师
44207 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
网络编程实战
15
15
1.0x
00:00/00:00
登录|注册

31丨性能篇答疑:epoll源码深度剖析

Level-triggered处理
事件传递
休眠唤醒
超时处理
回调函数设置
资源分配和初始化
whead
wait
base
llink
event
ws
fllink
ep
pwqlist
nwait
ffd
next
rdllink
rbn
visited
file
user
ws
ovflist
rbr
rdllist
poll_wait
wq
mtx
lock
事件处理效率
内核空间内存申请
ep_poll
查找epoll实例
参数验证
ep_poll_callback
ep_insert
红黑树查找
查找epoll实例
匿名文件和文件描述字分配
内存空间分配
参数验证
eppoll_entry
epitem
eventpoll
总结
epoll VS poll/select
epoll_wait
epoll_ctl
epoll_create
基本数据结构
性能篇答疑:epoll源码深度剖析

该思维导图由 AI 生成,仅供参考

你好,我是盛延敏,今天是网络编程实战性能篇的答疑模块,欢迎回来。
在性能篇中,我主要围绕 C10K 问题进行了深入剖析,最后引出了事件分发机制和多线程。可以说,基于 epoll 的事件分发能力,是 Linux 下高性能网络编程的不二之选。如果你觉得还不过瘾,期望有更深刻的认识和理解,那么在性能篇的答疑中,我就带你一起梳理一下 epoll 的源代码,从中我们一定可以有更多的发现和领悟。
今天的代码有些多,建议你配合文稿收听音频。

基本数据结构

在开始研究源代码之前,我们先看一下 epoll 中使用的数据结构,分别是 eventpoll、epitem 和 eppoll_entry。
我们先看一下 eventpoll 这个数据结构,这个数据结构是我们在调用 epoll_create 之后内核侧创建的一个句柄,表示了一个 epoll 实例。后续如果我们再调用 epoll_ctl 和 epoll_wait 等,都是对这个 eventpoll 数据进行操作,这部分数据会被保存在 epoll_create 创建的匿名文件 file 的 private_data 字段中。
/*
* This structure is stored inside the "private_data" member of the file
* structure and represents the main data structure for the eventpoll
* interface.
*/
struct eventpoll {
/* Protect the access to this structure */
spinlock_t lock;
/*
* This mutex is used to ensure that files are not removed
* while epoll is using them. This is held during the event
* collection loop, the file cleanup path, the epoll file exit
* code and the ctl operations.
*/
struct mutex mtx;
/* Wait queue used by sys_epoll_wait() */
//这个队列里存放的是执行epoll_wait从而等待的进程队列
wait_queue_head_t wq;
/* Wait queue used by file->poll() */
//这个队列里存放的是该eventloop作为poll对象的一个实例,加入到等待的队列
//这是因为eventpoll本身也是一个file, 所以也会有poll操作
wait_queue_head_t poll_wait;
/* List of ready file descriptors */
//这里存放的是事件就绪的fd列表,链表的每个元素是下面的epitem
struct list_head rdllist;
/* RB tree root used to store monitored fd structs */
//这是用来快速查找fd的红黑树
struct rb_root_cached rbr;
/*
* This is a single linked list that chains all the "struct epitem" that
* happened while transferring ready events to userspace w/out
* holding ->lock.
*/
struct epitem *ovflist;
/* wakeup_source used when ep_scan_ready_list is running */
struct wakeup_source *ws;
/* The user that created the eventpoll descriptor */
struct user_struct *user;
//这是eventloop对应的匿名文件,充分体现了Linux下一切皆文件的思想
struct file *file;
/* used to optimize loop detection check */
int visited;
struct list_head visited_list_link;
#ifdef CONFIG_NET_RX_BUSY_POLL
/* used to track busy poll napi_id */
unsigned int napi_id;
#endif
};
你能看到在代码中我提到了 epitem,这个 epitem 结构是干什么用的呢?
每当我们调用 epoll_ctl 增加一个 fd 时,内核就会为我们创建出一个 epitem 实例,并且把这个实例作为红黑树的一个子节点,增加到 eventpoll 结构体中的红黑树中,对应的字段是 rbr。这之后,查找每一个 fd 上是否有事件发生都是通过红黑树上的 epitem 来操作。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深度剖析了Linux下高性能网络编程中的关键技术——epoll的源码实现。通过对epoll_create、epoll_ctl等关键函数的分析,揭示了epoll如何高效地管理文件描述字和事件,以及内核中事件回调的原理。文章还从实现角度比较了epoll与poll/select的效率差异,指出epoll通过红黑树和就绪事件列表的维护,避免了大量内存申请和释放的操作,大大提高了性能。通过对比poll/select的实现,突显了epoll克服了poll/select的弊端,成为Linux下高性能网络编程的皇冠。总体而言,本文为读者提供了深入理解Linux下高性能网络编程中epoll技术的重要参考,对于从事网络编程或对Linux内核感兴趣的技术人员具有较高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《网络编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(18)

  • 最新
  • 精选
  • herongwei
    这篇文章写得非常棒!之前学 epoll,只会流于表面,也很少去深度剖析底层的数据结构,多读几遍! 另外分享一个大佬同学 epoll 内核源码详解发的帖子:https://www.nowcoder.com/discuss/26226

    作者回复: 赞分享。

    2019-10-26
    2
    22
  • 鱼向北游
    select这种是把等待队列和就绪队列混在一起,epoll根据这两种队列的特性用两种数据结构把这两个队列分开,果然在程序世界没有解决不了的事情,如果有,就加一个中间层

    作者回复: 你这个理解倒是比较有趣,程序是伟大的。

    2019-10-18
    19
  • HerofH
    首先感谢老师的精彩分析。 然后说下我的个人理解:epoll之所以效率比select高,原因在于select要做的事情太多,每次调用select不仅需要将描述符集添加到监听队列中,还要负责监听事件发生后的处理,以及select最后的清理工作等等... 而epoll则把这么多事情分到了epoll_ctl和epoll_wait去处理,调用epoll_ctl可以开启事件的监听工作,epoll_wait则可以完成对已激活的事件的处理工作,最后close(epfd)完成epoll的清理工作。 而select和epoll_wait是循环中反复调用的,epoll_wait做的事情比select少多了,因此从这个角度来说epoll效率会比select效率高。 除此之外,个人感觉epoll还有一个妙处就是就绪链表,当监听的事件发生后,相应的epitem会自动在监听回调中将其添加到就绪链表中,而对于select来说,则需要不停对所有监听的描述符进行遍历,来检查它们的状态。 不知道理解是否正确,敬请指正。

    作者回复: 👍

    2020-01-15
    9
  • 凉人。
    感觉这一章是最难以理解,如果多一些结构图,流程图,会好很多。

    作者回复: 嗯,好建议,记下了。

    2020-02-11
    8
  • fackgc17
    可以提供一下分析用的 kernel 版本吗

    作者回复: linux 4.14.4

    2019-10-26
    4
  • heyman
    请问一下,为什么要用红黑树?是因为要排序吗?排序的意义又在哪里?确保查找、插入和删除的高效还不够吗?

    作者回复: 就是要确保查找、插入和删除的高效啊,要知道,10K以上的连接时,这个对性能要求非常苛刻,一个高效的数据结构对于完成上述操作是非常关键的。

    2020-04-18
    4
    3
  • 瓜牛
    还是没明白为啥epoll有红黑树之后就不用在用户空间和内核空间之间拷贝了,或者说poll/select为啥要在用户空间和内核空间之间拷贝?

    作者回复: 简单的说,poll/select需要每次循环判断出有事件的fd,这份信息是一个全量的拷贝;而epoll则直接拿到事件发生后的信息。一个是ALL,一个是局部变化的部分,你说少没少拷贝?

    2021-11-05
    2
  • 功力不够,读起来有些费劲,好似拿到了九阴真经,不过需要黄姑娘翻译一下! 如果能来个图,就好了😁

    作者回复: 待我找黄姑娘来:)

    2019-12-01
    2
  • xupeng1644
    老师 在Level-triggered VS Edge-triggered小节中给出了Level-triggered的实现机制,可以再给下Edge-triggered的吗

    作者回复: Edge-triggered不需要特殊处理,就是有事件通知一次结束,这里Level-triggered还需要把没有完结的事件在拷贝一次通知给应用程序,所以需要特殊处理下。

    2020-02-28
    1
  • J.M.Liu
    老师,我请教两个问题: 1.ep_send_events_proc这个函数在把events列表拷贝到用户空间前会调用ep_item_poll函数,已确定对应的fd上的事件依旧有效。那ep_item_poll是根据什么来确定事件的有效性呢? 2.在ep_send_events_proc处理level-triggered的时候,有这么一段话“At this point, no one can insert into ep->rdllist besides us. The epoll_ctl() callers are locked out by ep_scan_ready_list() holding "mtx" and the poll callback will queue them in ep->ovflist.”意思是说epoll_ctl()的调用者也被锁在外面了。这个锁是说在ep_send_events_proc还没处理完的时候,epoll_ctl()无法操纵rdllist,但是之后是可以的,以实现再次注册感兴趣的时间。是这样吗?

    作者回复: 看得好自信,汗,我试着回答哈: 1.根据套接字文件上的poll接口,套接字文件上记录了它有效的事件: static inline unsigned int ep_item_poll(struct epitem *epi, poll_table *pt) { pt->_key = epi->event.events; return epi->ffd.file->f_op->poll(epi->ffd.file, pt) & epi->event.events; } 2.是的。 static int ep_scan_ready_list(struct eventpoll *ep, int (*sproc)(struct eventpoll *, struct list_head *, void *), void *priv, int depth, bool ep_locked) { int error, pwake = 0; unsigned long flags; struct epitem *epi, *nepi; LIST_HEAD(txlist); /* * We need to lock this because we could be hit by * eventpoll_release_file() and epoll_ctl(). */ if (!ep_locked) mutex_lock_nested(&ep->mtx, depth); ... }

    2019-10-29
    1
收起评论
显示
设置
留言
18
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部