
epoll / poll / select 三者都是用户态调用的 IO 多路复用系统调用。都属于用户态发起内核做事件等待。1、epoll调用epoll_create内核就会生成一个struct eventpoll对象同时返回一个 epfd文件描述符。epoll 实例本身也是一个文件是专门用来管理 IO 监听的虚拟文件。epoll 实例有两个核心成员rbr 红黑树根节点和 rdllist 就绪链表双向链表。rdllist 存放已经发生 IO 事件、已经就绪的 epitem 节点。当 socket 收到数据会把对应的 epitem 节点的 rdllink 挂到 rdllist 节点上这就是 epoll 高性能的根源只遍历已经就绪的 fd而不是全部 fd//epoll 总管理对象epoll_create 创建structeventpoll{structrb_rootrbr;// 红黑树根存放所有注册监听的 fd 节点structlist_headrdllist;// 就绪双向链表存放已经触发 IO 事件的 fd 节点};// 单个 fd 的监听条目核心节点 epitemstructepitem{structrb_noderbn;// 内嵌红黑树节点挂入 eventpoll.rbrstructlist_headrdllink;// 内嵌链表节点事件就绪时挂入 rdllistintfd;// 用户态 fd 数字structepoll_eventevent;// 监听事件 EPOLLIN/EPOLLOUT};/* epitem 本身不是红黑树节点 struct rb_node rbn红黑树节点真正的对象是嵌在 epitem 内部的一个成员 */structepitem├─rbn(rb_node红黑树用的指针)├─ rdllink ← rdllink 本体 │ ├─prev(structlist_head*)指向前一个rdllink │ └─next(structlist_head*)指向后一个rdllink ├─ fd └─ event/* epitem 是一个外壳大容器里面包含了红黑树节点 rbn 同理rdllink 是内嵌的 list_head也就是钩双向链表的小钩子也属于 epitem 内部成员 epitem 本体不会移动、不会拷贝只是它身上两个不同的“挂件/钩子”挂靠到两条不同的数据结构 1、rbn 这个挂件只要没有 EPOLL_CTL_DEL就永远挂在 eventpoll.rbr 红黑树上从不摘下 2、rdllink 这个小钩子可挂上、可摘下就绪链表 rdllist 事件来了就挂上epoll_wait处理完就摘下来悬空 3、一个 epitem借助内部两个内嵌节点可以同时存在于红黑树又临时挂入就绪链表 */┌────────────────────────────────────┐ │structepitem(大外壳)│ │ ┌──────────┐ ┌─────────────┐ │ │ │rb_node rbn│ │list_head rdllink││ │ │红黑树挂件 │ │就绪链表小钩子 ││ │ └──────────┘ └─────────────┘ │ │ fd、event等其他字段 │ └────────────────────────────────────┘socketsocket()系统调用创建内核对象struct socket也对应一个 fd一个 socket 可以被添加到一个或者多个 epoll 实例中一个 epoll 实例可以监听成千上万个 socket fd/* 一个 epoll 实例可以监听成千上万条 TCP 连接这就是 epoll 高并发的核心 */eventpoll(epoll实例)├─红黑树rbr ├ epitem1 → TCP连接1socket fd4 ├ epitem2 → TCP连接2socket fd5 ├ epitem3 → TCP连接3socket fd6 └...成千上万epitem └─rdllist就绪链表只放当前有IO事件的epitem