深入理解NIO(四)—— epoll的实现原理
本文链接:https://www.cnblogs.com/fatmanhappycode/p/12362423.html
终于来到最后了,万里长征只差最后一步 ( `д´) !
我们先从只监听一个socket开始讲起:
首先我们有一个程序A,他运行这下面这样一段代码:
//创建socket int s = socket(AF_INET, SOCK_STREAM, 0); //绑定端口 bind(s, ...) //监听 listen(s, ...) //接受客户端连接 int c = accept(s, ...) //接收客户端数据,没有数据就先阻塞在这里 recv(c, ...); //将数据打印出来 printf(...)
当程序A运行到recv()的时候它阻塞了,
之后就挂起在等待队列中,等待被唤醒之后继续执行,
而在Linux中,万事万物皆为文件,我们的socket也占用了一个fd。
我们的A就挂在socket中的等待队列中。
接下来我们来看一个简单的流程:
//将数据打印出来 printf(...)
当然,实际应用中我们不可能只监听一个socket,接下来我们就直接来看一下,能监听多个socket的select是怎么实现的:
select:上世纪 80 年代就实现了,它支持注册 FD_SETSIZE(1024) 个 socket,在那个年代肯定是够用的,不过现在嘛,肯定是不行了。
当然,这里的select不是指nio里的select()方法,而是指操作系统中的一个实现,它的改进版本是poll和epoll,请大家注意不要混淆。
我们先从select说起:
int s = socket(AF_INET, SOCK_STREAM, 0); bind(s, ...); listen(s, ...); int fds[] = // 存放需要监听的socket; while(1){ // 这里就是我们的select int n = select(..., fds, ...) for(int i=0; i < fds.count; i++){ if(FD_ISSET(fds[i], ...)){ //fds[i]的数据处理 } } }
这段代码中,先准备了一个数组 fds,让 fds 存放着所有需要监视的 socket。然后调用 select,如果 fds 中的所有 socket 都没有数据,select 会阻塞,直到有一个 socket 接收到数据,select 返回,唤醒进程。用户可以遍历 fds,通过 FD_ISSET 判断具体哪个 socket 收到数据,然后做出处理。
接下来我们看一下流程图:
select的缺点在于:
因此人们先提出了poll
poll:1997 年,出现了 poll 作为 select 的替代者,最大的区别就是,poll 不再限制 socket 数量。
但是poll并没有解决刚刚提到的select的问题,所以就有了epoll
select 低效的原因之一是将“维护等待队列”和“阻塞进程”两个步骤合二为一。如下图所示,每次调用 select 都需要这两步操作,然而大多数应用场景中,需要监视的 socket 相对固定,并不需要每次都修改。epoll 将这两个操作分开,先用 epoll_ctl 维护等待队列,再调用 epoll_wait 阻塞进程。显而易见地,效率就能得到提升。
还记得我们的NIO源码分析吗,里面就调用了epoll_create、epoll_ctl和epoll_wait
当某个进程调用 epoll_create 方法时,内核会创建一个 eventpoll 对象(也就是程序中 epfd 所代表的对象)。eventpoll 对象也是文件系统中的一员,和 socket 一样,它也会有等待队列。
如果通过 epoll_ctl 添加 sock1、sock2 和 sock3 的监视,内核会将 eventpoll 添加到这三个 socket 的等待队列中。
进程 A 运行到了 epoll_wait 语句。如下图所示,内核会将进程 A 放入 eventpoll 的等待队列中,阻塞进程。(这里的rdlist和等待队列都是eventpoll对象的属性一样的东西,都是属于它的)
当 socket 接收到数据,中断程序一方面修改 rdlist,另一方面唤醒 eventpoll 等待队列中的进程,进程 A 再次进入运行状态(如下图)。也因为 rdlist 的存在,进程 A 可以知道哪些 socket 发生了变化。
监听所有socket的等待队列(不是存放进程A的)的结构是红黑树,红黑树是一种自平衡二叉查找树,搜索、插入和删除时间复杂度都是O(log(N)),效率较好
而rdlist是一个双向链表,进程A应该是放在poll_wait里(我猜的,错了不怪我)
终于结束了 ( `д´) !经过四篇深入理解NIO系列博文,你已经是个成熟Java调包师了,你将和我一样找不到工作,おめでとう !
本文参考自 : https://my.oschina.net/editorial-story/blog/3052308#comments,我很喜欢这种讲解风格,不过原博主好像主要写游戏开发类的,可能很难再有我想看的博文了。
原文:https://www.cnblogs.com/fatmanhappycode/p/12362423.html