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

为什么需要自己实现 epoll 呢?现在自己打算做一个用户态的协议栈。采用单线程的模式。https://github.com/wangbojing/NtyTcp,至于为什么要实现用户态协议栈?可以自行百度 C10M 的问题。

由于协议栈做到了用户态故需要自己实现高性能网络 IO 的管理。所以 epoll 就自己实现一 下。代码:https://github.com/wangbojing/NtyTcp/blob/master/src/nty_epoll_rb.c

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

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

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

3. Epoll 内核回调。

4. Epoll 的 LT(水平触发)与 ET(边沿触发)。

下面从这四个方面来实现 epoll:

一、 Epoll 数据结构

Epoll 主要由两个结构体:eventpoll 与 epitem。Epitem 是每一个 IO 所对应的的事件。比如 epoll_ctl EPOLL_CTL_ADD 操作的时候,就需要创建一个 epitem。Eventpoll 是每一个 epoll 所 对应的的。比如 epoll_create 就是创建一个 eventpoll。

Epitem 的定义:

​数据结构如下图所示:

​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。

List 删除:

​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 减1。避免 SMP 体系下,多核竞争。此处采用自旋锁,不适合采用睡眠锁。

【文章福利】另外小编还整理了一些C++后端开发面试题,教学视频,后端学习路线图免费分享,需要的可以自行添加:学习交流群点击加入~ 群文件共享

小编强力推荐C++后端开发免费学习地址:C/C++Linux服务器开发高级架构师/C++后台开发架构师​

​Rbtree 的添加:

​149 行:获取互斥锁。

153 行:查找 sockid 的 epitem 是否存在。存在则不能添加,不存在则可以添加。

160 行:分配 epitem。

167 行:sockid 赋值

168 行:将设置的 event 添加到 epitem 的 event 域。

170 行:将 epitem 添加到 rbrtree 中。

173 行:释放互斥锁。

Rbtree 删除:

​177 行:获取互斥锁。

181 行:删除 sockid 的结点,如果不存在,则 rbtree 返回-1。

188 行:释放 epitem 。

190 行:释放互斥锁。

Epoll_wait 的挂起:

Epoll_wait的挂起采用 pthread_cond_wait,具体实现可以参照:https://github.com/wangbojing/NtyTcp/blob/master/src/nty_epoll_rb.c。

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

参考资料

推荐一个零声教育C/C++后台开发的免费公开课程,个人觉得老师讲得不错,分享给大家:C/C++后台开发高级架构师,内容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

原文:【高级架构】用户态协议栈之Epoll实现原理

【高阶知识】用户态协议栈之Epoll实现原理相关推荐

  1. 用户态协议栈之epoll实现

    1 epoll编程问题 对于数据传输send/recv这些接口的而言:如何判断send数据,对方有没有接收成功? 采用send返回值无意义.,send发送数据只是将数据拷贝到协议栈,而数据的发送是协议 ...

  2. 【Linux服务器开发系列】手写用户态协议栈,udpipeth数据包的封装,零拷贝的实现,柔性数组

    视频教你手写网络协议栈,保证大家能学会,耐心看 1. 用户态协议栈 2. udp/ip/eth数据包的封装 3. 零拷贝的实现 4. 零长数组(柔性数组) [Linux服务器开发系列]手写用户态协议栈 ...

  3. 学习笔记:MySQL高阶知识体系(下)——索引、锁、日志、隔离级别与MVCC

    转载自https://www.ydlclass.com/doc21xnv/database/mysqladvance/mysqlAdvance2.html MySQL高阶知识体系(下) 6. 索引 6 ...

  4. 架构决定可扩展性--聊聊用户态协议栈的意义

    在进入这个话题之前先说说通用和专业之间的区别.   举个很好的例子,好比我们个人,绝大部分的人都是"通用"的,而只有极少部分的人是"专业"的.通用的人主要目标是 ...

  5. 用户态协议栈设计实现

    目录 1. 网络模式 2. 获取原始数据 3. UDP数据帧格式 以太网协议头(数据链路层) -- 14byte IP数据头(网络层) -- 20byte UDP协议的数据格式(传输层) -- 8by ...

  6. FD.io VSAP(VPP Stack Acceleration Project),通过FD.io VSAP构建用户态协议栈

    目录 VSAP Scope-使用范围 Releases List of all subpages (used or unused) 通过FD.io VSAP构建用户态协议栈 VSAP https:// ...

  7. 用户态协议栈之tcp/ip设计

    1 解决问题 对于服务器而言,正常的接受一帧Data的过程,客户端先通过网络发送一帧数据到网卡,再经过协议栈,最后通过系统调用叨叨应用程序.具体的流程图如下: 针对上面的两个流程,涉及到两次拷贝(网卡 ...

  8. 用户态协议栈tcp/ip设计

    1 解决问题 对于服务器而言,正常的接受一帧Data的过程,客户端先通过网络发送一帧数据到网卡,再经过协议栈,最后通过系统调用叨叨应用程序.具体的流程图如下: 针对上面的两个流程,涉及到两次拷贝(网卡 ...

  9. 深入浅出用户态协议栈

    一.前言 在讲网络协议栈前,先理解一个数据包在网络传输是一个怎么样的流程,如下图所示. 正常的流程是网卡接收到数据后,把数据copy到协议栈(sk_buff),协议栈把sk_buff数据解析完后再把数 ...

最新文章

  1. 使用深度学习检测混凝土结构中的表面裂缝
  2. 求有环单链表的链表长
  3. Alpha 冲刺报告(3/10)
  4. 所选驱动器不再有效,继续安装之前,请先检查安装路径的设置
  5. you do not have permission
  6. 五个 .NET 性能小贴士
  7. totolinkn200up怎么设置_totolinkN200R无线路由器如何设置啊,求高人指点
  8. 爬取豆瓣电影储存到数据库MONGDB中以及反反爬虫
  9. struts2.0.14 web.xml,struts.xml配置
  10. 怎么把我的文档C盘的路径改到E盘啊
  11. C++程序设计一(进制转换)
  12. 在html插入数学公式,在网页中显示数学公式
  13. COM组件和一般DLL 的区别
  14. IDEA初始jsp模板和修改jsp模板
  15. 鼠标划过切换div显示
  16. 微信小程序云存储(文件上传到云端)
  17. day 22 内置的模块
  18. Duality对偶学习笔记(第一课时)
  19. java中直线距离的计算_java计算两点间的距离方法总结
  20. 百度为手游盛世添把火

热门文章

  1. html点击按钮弹出悬浮窗_javascript如何实现弹出浮动窗口
  2. oracle宿主常量及变量,oracle入门札记
  3. Simulink之脉宽调制(PWM)逆变技术
  4. 店盈通:拼多多店铺该怎么运营?店铺运营思路解析
  5. 万丈高楼平地起,打好地基很关键(二)
  6. JAVA设计模式是个什么玩意儿_04_建造者模式
  7. TX9118 同步升压IC
  8. idea中集成http请求
  9. 中文汉字html(立→超文本)用汉字写网页代码
  10. [Arduino]关于arduino板上Atmel mega16U2芯片,转载【认识Arduino UNO之三: UNO原理图解读(认识ATmega8U2和ATmega16U2)】