epoll 的实现原理

epoll 是Linux IO 多路复用的管理机制。作为现在Linux平台高性能网络IO必要的组件。内核的实现可以参考: fs/eventpoll.c

在实现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
在这里插入图片描述
数据结构如下:
在这里插入图片描述
List 用来存储准备就绪的IO,对于数据结构主要讨论两方面:insert与remove。同样如此,对于list我们也讨论insert与remove。何时将数据插入到list中呢?当内核io准备就绪的时候,则就会执行epoll_event_callback的回调函数,将epitem添加到list中。
那何时删除list中的元素呢?当epoll_wait 激活重新运行的时候,将list的epitem逐一copy到events参数中。
Rbtree用来存储所有io的数据,方便快速通io_fd查找。也从insert与remove来讨论。对于rbtree何时添加:当App执行epoll_ctl EPOLL_CTL_ADD操作,当epitem添加到rbtree中。何时删除呢?当App执行epoll_ctl EPOLL_CTL_DEL操作,将epitem添加到rbtree中。List 与rbtree的操作又如何做到线程安全,SMP ,防止死锁呢?

Epoll锁机制

Epoll从以下几个方面是需要加锁保护的。List的操作,rbtree的操作,epoll_wait的等待。List使用最小粒度的锁spinlock,便于在SMP下添加操作的时候,能够快速操作list。list添加
在这里插入图片描述
346 行:获取 spinlock。
347 行:epitem 的 rdy 置为 1,代表 epitem 已经在就绪队列中,后续再触发相同事件就只需
更改 event。
348 行:添加到 list 中。
349 行:将 eventpoll 的 rdnum 域 加 1。
350 行:释放 spinlock
在这里插入图片描述
301 行:获取 spinlock
304 行:判读 rdnum 与 maxevents 的大小,避免 event 溢出。
307 行:循环遍历 list,判断添加 list 不能为空
309 行:获取 list 首个结点
310 行:移除 list 首个结点。
311 行:将 epitem 的 rdy 域置为 0,标识 epitem 不再就绪队列中。
313 行:copy epitem 的 event 到用户空间的 events。
316 行:copy 数量加 1
317 行:eventpoll 中 rdnum 减一。
避免 SMP 体系下,多核竞争。此处采用自旋锁,不适合采用睡眠锁。
在这里插入图片描述
149 行:获取互斥锁。
153 行:查找 sockid 的 epitem 是否存在。存在则不能添加,不存在则可以添加。
160 行:分配 epitem。
167 行:sockid 赋值
168 行:将设置的 event 添加到 epitem 的 event 域。
170 行:将 epitem 添加到 rbrtree 中。
173 行:释放互斥锁。
在这里插入图片描述
177 行:获取互斥锁。
181 行:删除 sockid 的结点,如果不存在,则 rbtree 返回-1。
188 行:释放 epitem
190 行:释放互斥锁。

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 事件。
在这里插入图片描述
在这里插入图片描述

LT与ET

LT(水平触发)与 ET(边沿触发)是电子信号里面的概念。不清楚可以 man epoll 查看的。
如下图所示:
在这里插入图片描述
比如: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、付费专栏及课程。

余额充值