手把手设计实现epoll之原理详解

Epoll 是 Linux IO 多路复用的管理机制。 作为现在 Linux 平台高性能网络 IO 必要的组件。所有的linux服务器底层全部用的epoll linux在以前只能做嵌入式设备,但是自从有了epoll的存在,就有了linux可以用来做服务器。
对于epoll的实现主要考虑以下四个方面即可:


一、 epoll实现的数据结构

epoll中fd的存储是典型的通过key-value的结构(通过fd找到对应的事件)
对于存储的fd分为两种集合:对于所有fd的总集和就绪的fd的集合。
对于所有的fd的总集可选的存储结构有:

  • 数组:需设置固定大小的空间。空间有限,查找速度慢(需要遍历)
  • hash:开始创建内存消耗非常大,成本比较高。fd数量不确定贸然使用也不是明智之举,空间浪费比较多。fd数量大的时候性能非常好。
  • B树/B+树:主要是用于磁盘上面,mysql,层高比较低,查找效率对比次数不会比红黑树小。
  • 红黑树:优于avl树,适合内存查找。查找效率和空间利用率较高。因此选用红黑树来存储

就绪的fd的集合可选的存储结构有:
就绪的事件都需要处理没有优先级处理,类属于轻重缓急中的重。推荐选择使用线性的数据结构,队列和栈。
为什么选择队列?
栈是先进后出,担心就绪的fd多的时候,最先的数据从来不能被处理。

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、协议栈与epoll模块通信

epoll的工作模块

在这里插入图片描述
协议栈解析出有数据来,通知到epoll中。应用程序操作epoll

协议栈与epoll通信

在这里插入图片描述

协议栈和epoll通信的时机?

如上图所示
1 当三次握手后,服务器的全连接队列(accept队列)中增加一个节点,协议栈会通知到epoll
2 当服务器协议栈接收到数据并且回复确认消息后,也会通知epoll 一个epollin(可写)的消息
3 当snedbuffer中数据以mss为单位发送出去时候,当发送一个mss数据,并且收到这个mss数据返回的ack消息,此时再sendbuffer中将这个mss的数据从sendbuffer中删除,并且协议栈会通知epoll一个epollout(可读的消息)
4 当服务器接收到一个fin标志位的时候,也会通知给epoll一个epollin的消息
5 当有收到rst标志位的时候,协议栈会通知给epoll一个epollerr的信号

红黑树的结点和就绪队列的结点是一个结点

协议栈通知epoll的方式

通过回调函数的方式通知epoll
回调函数要做哪些事情呢?回调函数时传入的参数 fd,epollin/epollout/epollerr
1 通过fd查找对应的节点
2 把节点加入到就绪队列
协议栈 通知epoll 和epoll通知用户态两两之间没有耦合,都是异步的关系
协议栈调用回调函数时会调用pthread_cond_signal 通知epoll_wait函数中的pthread_cond_wait(条件等待) 往下面走
内核可以通知用户态,但是用户态不能够通知内核。

三、 epoll如何加锁

epoll 是通过锁来保证线程安全的,多线程同时操作一个epoll
加锁方式:
1 epoll_ctl是对红黑树加锁:
有两种加锁方法:1 对整棵树进行加锁 2锁子树
锁子树是很麻烦的事,不允许其他的老操作。
红黑树节点比较多,使用互斥锁

2.epoll_wait 是需要对就绪队列进行加锁:
就绪队列就好比生产消费模式。协议栈调用回调函数相当于是生产模式,消费模式就是调用epoll_wait.对于队列而言一般用自旋锁比较合适。对于队列添加时,几条指令比较简单。互斥锁没有竞争到资源会让出cpu,而自旋锁不会。自旋锁并不会让出cpu,因为让出cpu的成本更高。
互斥锁与自旋锁分析

四、et与lt如何实现

  1. et与lt的区别
    et 边沿触发:当调用recv函数如果没有把协议栈中的recvbuffr中的数据全部读完,就不会继续触发,当客户端再发数据的时候,才会再次被触发。
    lt水平触发:当调用recv函数如果没有把协议栈中的recvbuffr中的数据全部读完,就回一直触发,直到全部读完为止。

  2. 为什么会有水平触发和边沿触发?
    水平触发和边沿触发不是一开始就故意设计出来的,其理念来自于嵌入式的电平的高低变化

  3. 如何实现水平触发和边沿触发?
    et从协议栈中检测到recvbuffer中接收数据就调用回调,水平触发检测recvbuffer有数据就调回调,平触发和边沿触发代码实现核心是内核通知epoll时执行回调函数的次数的区别。

ET 和 LT 关于sendbuffer的特殊情况。同时监听读事件和写事件时,
当sendbuffer为空的时候,ET回一直触发,就和LT的功能很相似。

五、epoll_wait

epoll_wait的第二个参数epoll_event数组是用户态的,会把内核态的链表中的数据拷贝过来,拷贝出来就会移除在内核态

补充:select/poll 与epoll的区别

1 使用:select/poll需要把总集拷贝到内核中,epoll不用
2 实现原理:select/poll 循环遍历总集,是否需要有就绪 ,epoll不需要

关于select/poll的实现机制请参考:select机制

参考:服务器课程

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值