拿select模型为例,假设我们的做事器须要支持100万的并发连接,则在__FD_SETSIZE 为1024的情形下,则我们至少须要开辟1k个进程才能实现100万的并发连接。除了进程间高下文切换的韶光花费外,从内核/用户空间大量的无脑内存拷贝、数组轮询等,是系统难以承受的。因此,基于select模型的做事器程序,要达到10万级别的并发访问,是一个很难完成的任务。
因此,该epoll上场了。
详细教程资料关注+后台私信;资料;两个字可以免费视频领取+文档+各大厂口试题 资料内容包括:C/C++,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,嵌入式 等。

由于epoll的实现机制与select/poll机制完备不同,上面所说的 select的缺陷在epoll上不复存在。
设想一下如了局景:有100万个客户端同时与一个做事器进程保持着TCP连接。而每一时候,常日只有几百上千个TCP连接是生动的(事实上大部分场景都是这种情形)。如何实现这样的高并发?
在select/poll时期,做事器进程每次都把这100万个连接见告操作系统(从用户态复制句柄数据构造到内核态),让操作系统内核去查询这些套接字上是否有事宜发生,轮询完后,再将句柄数据复制到用户态,让做事器运用程序轮询处理已发生的网络事宜,这一过程资源花费较大,因此,select/poll一样平常只能处理几千的并发连接。
epoll的设计和实现与select完备不同。epoll通过在Linux内核中申请一个大略单纯的文件系统(文件系统一样平常用什么数据构造实现?B+树)。把原来的select/poll调用分成了3个部分:
1)调用epoll_create()建立一个epoll工具(在epoll文件系统中为这个句柄工具分配资源)
2)调用epoll_ctl向epoll工具中添加这100万个连接的套接字
3)调用epoll_wait网络发生的事宜的连接
如此一来,要实现上面说是的场景,只须要在进程启动时建立一个epoll工具,然后在须要的时候向这个epoll工具中添加或者删除连接。同时,epoll_wait的效率也非常高,由于调用epoll_wait时,并没有一股脑的向操作系统复制这100万个连接的句柄数据,内核也不须要去遍历全部的连接。
Epoll 很主要,但是 Epoll 与 Select 的差异是什么呢?Epoll 高效的缘故原由是什么?
从网卡吸收数听说起
详细教程资料关注+后台私信;资料;两个字可以免费视频领取+文档+各大厂口试题 资料内容包括:C/C++,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,嵌入式 等。
下边是一个范例的打算机构造图,打算机由 CPU、存储器(内存)与网络接口等部件组成,理解 Epoll 实质的第一步,要从硬件的角度看打算机若何吸收网络数据。
打算机构造图(图片来源:Linux 内核完备注释之微型打算机组成构造)
下图展示了网卡吸收数据的过程:
在 1 阶段,网卡收到网线传来的数据。经由 2 阶段的硬件电路的传输。终极 3 阶段将数据写入到内存中的某个地址上。这个过程涉及到 DMA 传输、IO 通路选择等硬件有关的知识,但我们只需知道:网卡会把吸收到的数据写入内存。
网卡吸收数据的过程
通过硬件传输,网卡吸收的数据存放到内存中,操作系统就可以去读取它们。
如何知道吸收了数据?
理解 Epoll 实质的第二步,要从 CPU 的角度来看数据吸收。理解这个问题,要先理解一个观点:中断。
打算机实行程序时,会有优先级的需求。比如,当打算机收到断电旗子暗记时,它应立即去保存数据,保存数据的程序具有较高的优先级(电容可以保存少许电量,供 CPU 运行很短的一小段韶光)。
一样平常而言,由硬件产生的旗子暗记须要 CPU 立马做出回应,不然数据可能就丢失了,以是它的优先级很高。
CPU 理应中断掉正在实行的程序,去做出相应;当 CPU 完成对硬件的相应后,再重新实行用户程序。
中断的过程如下图,它和函数调用差不多,只不过函数调用是事先定好位置,而中断的位置由“旗子暗记”决定。
中断程序调用
以键盘为例,当用户按下键盘某个按键时,键盘会给 CPU 的中断引脚发出一个高电平,CPU 能够捕获这个旗子暗记,然后实行键盘中断程序。
下图展示了各种硬件通过中断与 CPU 交互的过程:
当网卡把数据写入到内存后,网卡向 CPU 发出一个中断旗子暗记,操作系统便能得知有新数据到来,再通过网卡中断程序去处理数据。
epoll的线程安全设计
1.对rbtree-->加锁;2.对queue -->spinlock3.epoll_wati,cond,mutex
开源项目中的ET和LT的选择
ET模式由于ET模式只有从unavailable到available才会触发,以是 读事宜:须要利用while循环读取完,一样平常是读到EAGAIN,也可以读到返回值小于缓冲区大小;如果运用层读缓冲区满:那就须要运用层自行标记,办理OS不再关照可读的问题 写事宜:须要利用while循环写到EAGAIN,也可以写到返回值小于缓冲区大小如果运用层写缓冲区空(无内容可写):那就须要运用层自行标记,办理OS不再关照可写的问题。 LT模式由于LT模式只要available就会触发,以是: 读事宜:由于一样平常运用层的逻辑是“来了就能读”,以是一样平常没有问题,无需while循环读取到EAGAIN;如果运用层读缓冲区满:就会常常触发,办理办法如下; 写事宜:如果没有内容要写,就会常常触发,办理办法如下。LT常常触发读写事宜的办理办法:修正fd的注册事宜,或者把fd移出epollfd。 总结目前彷佛还是LT办法运用较多,包括redis,libuv. LT模式的优点在于:事宜循环处理比较大略,无需关注运用层是否有缓冲或缓冲区是否满,只管上报事宜缺陷是:可能常常上报,可能影响性能。 在我目前阅读的开源项目中:TARS用的是ET,Redis用的是LT,Nginx可配置(开启NGX_HAVE_CLEAR_EVENT--ET,开启NGX_USE_LEVEL_EVENT则为LT).