Epoll的实现原理

简介: 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定义

  • Eventpoll定义

数据结构示意图

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 事件。

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

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 回调函数。

相关文章
|
小程序 JavaScript 前端开发
小程序wx:if 和hidden的区别?
小程序wx:if 和hidden的区别?
|
算法 测试技术 编译器
掌握CTest:CTest综合指南
掌握CTest:CTest综合指南
999 1
|
人工智能 搜索推荐 异构计算
|
负载均衡 网络协议 安全
DPDK用户态协议栈-KNI
DPDK用户态协议栈-KNI
|
10月前
|
传感器
Modbus协议深入解析
Modbus协议是由Modicon公司(现施耐德电气)于1979年发明的串行通信协议,主要用于工业自动化系统中的PLC通信。本文深入解析了Modbus协议的主从模式、数据类型(线圈、离散输入、保持寄存器、输入寄存器)、帧结构和通信过程,并介绍了其应用场景和重要性。
388 0
|
12月前
|
存储 数据管理 调度
HarmonyOS架构理解:揭开鸿蒙系统的神秘面纱
【10月更文挑战第21天】华为的鸿蒙系统(HarmonyOS)以其独特的分布式架构备受关注。该架构包括分布式软总线、分布式数据管理和分布式任务调度。分布式软总线实现设备间的无缝连接;分布式数据管理支持跨设备数据共享;分布式任务调度则实现跨设备任务协同。这些特性为开发者提供了强大的工具,助力智能设备的未来发展。
599 1
|
监控 Java 数据库连接
Spring高手之路17——动态代理的艺术与实践
本文深入分析了JDK和CGLIB两种动态代理技术在Spring框架中的应用。讨论了动态代理的基础概念,通过实例展示了如何实现和应用这两种方法,并比较了它们的性能差异及适用场景。进一步,探讨了在动态代理中实现熔断限流和日志监控的策略,以及如何利用动态代理优化Spring应用的设计和功能。
245 6
Spring高手之路17——动态代理的艺术与实践
|
Linux Shell Android开发
用eadb在Android上搭建eBPF运行环境
用eadb在Android上搭建eBPF运行环境
|
存储 资源调度 分布式计算
在分布式数据库系统中处理大规模数据
【4月更文挑战第24天】在分布式数据库系统中处理大规模数据
271 3
|
数据可视化 前端开发 JavaScript
Java中的数据可视化与图表库选择
Java中的数据可视化与图表库选择