深入理解Linux IO复用之epoll( 二 )


  • 对fd数量没有限制(当然这个在poll也被解决了)
  • 抛弃了bitmap数组实现了新的结构来存储多种事件类型
  • 无需重复拷贝fd 随用随加 随弃随删
  • 采用事件驱动避免轮询查看可读写事件
综上可知,epoll出现之后大大提高了并发量对于C10K问题轻松应对,即使后续出现了真正的异步IO,也并没有(暂时没有)撼动epoll的江湖地位,主要是因为epoll可以解决数万数十万的并发量,已经可以解决现在大部分的场景了,异步IO固然优异,但是编程难度比epoll更大,权衡之下epoll仍然富有生命力 。
3.epoll的基本实现
  • epoll的api定义:

深入理解Linux IO复用之epoll

文章插图
 
  • epoll_create是在内核区创建一个epoll相关的一些列结构,并且将一个句柄fd返回给用户态,后续的操作都是基于此fd的,参数size是告诉内核这个结构的元素的大小,类似于stl的vector动态数组,如果size不合适会涉及复制扩容,不过貌似4.1.2内核之后size已经没有太大用途了;
  • epoll_ctl是将fd添加/删除于epoll_create返回的epfd中,其中epoll_event是用户态和内核态交互的结构,定义了用户态关心的事件类型和触发时数据的载体epoll_data;
  • epoll_wait是阻塞等待内核返回的可读写事件,epfd还是epoll_create的返回值,events是个结构体数组指针存储epoll_event,也就是将内核返回的待处理epoll_event结构都存储下来,maxevents告诉内核本次返回的最大fd数量,这个和events指向的数组是相关的;
  • epoll_event是用户态需监控fd的代言人,后续用户程序对fd的操作都是基于此结构的;
 
  • 通俗描述:
【深入理解Linux IO复用之epoll】可能上面的描述有些抽象,不过其实很好理解,举个现实中的例子:
  • epoll_create场景:
  • 大学开学第一周,你作为班长需要帮全班同学领取相关物品,你在学生处告诉工作人员,我是xx学院xx专业xx班的班长,这时工作人员确定你的身份并且给了你凭证,后面办的事情都需要用到(也就是调用epoll_create向内核申请了epfd结构,内核返回了epfd句柄给你使用);
  • epoll_ctl场景:
  • 你拿着凭证在办事大厅开始办事,分拣办公室工作人员说班长你把所有需要办理事情的同学的学生册和需要办理的事情都记录下来吧,于是班长开始在每个学生手册单独写对应需要办的事情:
  • 李明需要开实验室权限、孙大熊需要办游泳卡......就这样班长一股脑写完并交给了工作人员(也就是告诉内核哪些fd需要做哪些操作);
  • epoll_wait场景:
  • 你拿着凭证在领取办公室门前等着,这时候广播喊xx班长你们班孙大熊的游泳卡办好了速来领取、李明实验室权限卡办好了速来取....还有同学的事情没办好,所以班长只能继续(也就是调用epoll_wait等待内核反馈的可读写事件发生并处理);
  •  
  • 官方DEMO
通过man epoll可以看到官方的demo:
深入理解Linux IO复用之epoll

文章插图
 
深入理解Linux IO复用之epoll

文章插图
 
深入理解Linux IO复用之epoll

文章插图
 
在epoll_wait时需要区分是主监听线程fd的新连接事件还是已连接事件的读写请求,进而单独处理 。
4.epoll的底层实现epoll底层实现最重要的两个数据结构:epitem和eventpoll 。
可以简单的认为epitem是和每个用户态监控IO的fd对应的,eventpoll是用户态创建的管理所有被监控fd的结构,详细的定义如下:
深入理解Linux IO复用之epoll

文章插图
 
深入理解Linux IO复用之epoll

文章插图
 
  • 底层调用过程
epoll_create会创建一个类型为struct eventpoll的对象,并返回一个与之对应文件描述符,之后应用程序在用户态使用epoll的时候都将依靠这个文件描述符,而在epoll内部也是通过该文件描述符进一步获取到eventpoll类型对象,再进行对应的操作,完成了用户态和内核态的贯穿 。
epoll_ctl底层主要调用epoll_insert实现操作: