Epoll的实现原理

Epoll是Linux IO多路复用的一种IO管理机制。内核的实现代码在Linux内核源码的fs/eventpoll.c中。是比select和poll更高性能的一种IO管理机制。

前期准备

在实现epoll之前,要先了解内核epoll的运行原理。内核的epoll可以从四方面来理解。

  1. Epoll 的数据结构,rbtree 对<fd, event>的存储,ready 队列存储就绪 io。

  2. Epoll的线程安全,SMP的运行,以及防止死锁。

  3. Epoll内核回调

  4. Epoll的LT和ET

具体实现

Epoll数据结构

Epoll 主要由两个结构体:eventpoll 与 epitem。Epitem 是每一个 IO 所对应的的事件。比如

epoll_ctl EPOLL_CTL_ADD 操作的时候,就需要创建一个 epitem。Eventpoll 是每一个 epoll 所

对应的的。比如 epoll_create 就是创建一个 eventpoll。

  • Epitem定义

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240817225445.png

  • Eventpoll定义

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240817225536.png

数据结构示意图

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240817230146.png

List用来存储就绪的IO。当内核IO准备就绪的时候就会执行epoll_event_callback的回调函数,将epitem添加到List中。当epoll_wait激活重新运行的时候,将list的epitem逐一copy到events参数中。

RBtree用来存储所有io数据,方便快速通过io_fd查找。也从insert与remove来讨论。

当 App 执行 epoll_ctl EPOLL_CTL_ADD 操作,将 epitem 添加到 rbtree
中。当 App 执行 epoll_ctl EPOLL_CTL_ADD 操作,将 epitem 从rbtree中删除。

Epoll锁机制

// 获取自旋锁	
pthread_spin_lock(&ep->lock);
// epitem的rdy置1,代表epitem在就绪队列重,后续触发相同事件只需要修改event
epi->rdy = 1;
// 添加到list中
LIST_INSERT_HEAD(&ep->rdlist, epi, rdlink);
// 将eventpoll的rdnum加1
ep->rdnum ++;
// 释放spinlock
pthread_spin_unlock(&ep->lock);

Epoll回调

Epoll 的回调函数何时执行,此部分需要与 Tcp 的协议栈一起来阐述。Tcp 协议栈的时序图如

下图所示,epoll 从协议栈回调的部分从下图的编号 1,2,3,4。具体 Tcp 协议栈的实现,后续

从另外的文章中表述出来。下面分别对四个步骤详细描述

编号 1:是 tcp 三次握手,对端反馈 ack 后,socket 进入 rcvd 状态。需要将监听 socket 的

event 置为 EPOLLIN,此时标识可以进入到 accept 读取 socket 数据。

编号 2:在 established 状态,收到数据以后,需要将 socket 的 event 置为 EPOLLIN 状态。

编号 3:在 established 状态,收到 fin 时,此时 socket 进入到 close_wait。需要 socket 的 event
置为 EPOLLIN。读取断开信息。

编号 4:检测 socket 的 send 状态,如果对端 cwnd>0 是可以,发送的数据。故需要将 socket

置为 EPOLLOUT。

所以在此四处添加 EPOLL 的回调函数,即可使得 epoll 正常接收到 io 事件。

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/%E6%9F%A5%E7%9C%8B%E8%AF%84%E8%AE%BASequence.png

有点尴尬,这个图等我有空了再重新画一遍,我不知道他要vip才能无水印。

LT和ET

LT(水平触发)与 ET(边沿触发)是电子信号里面的概念。不清楚可以 man epoll 查看的。
如下图所示:

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240818002132.png

比如:event = EPOLLIN | EPOLLLT,将 event 设置为 EPOLLIN 与水平触发。只要 event 为 EPOLLIN

时就能不断调用 epoll 回调函数。

比如: event = EPOLLIN | EPOLLET,event 如果从 EPOLLOUT 变化为 EPOLLIN 的时候,就会触

发。在此情形下,变化只发生一次,故只调用一次 epoll 回调函数。关于水平触发与边沿触

发放在 epoll 回调函数执行的时候,如果为 EPOLLET(边沿触发),与之前的 event 对比,如

果发生改变则调用 epoll 回调函数,如果为 EPOLLLT(水平触发),则查看 event 是否为 EPOLLIN,

即可调用 epoll 回调函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值