一、epoll三个关键函数的实现原理
极简模型
如下图,一个简单基本的例子,在这段代码中,我们使用epoll处理数据的一个过程
服务端通过accept调用接收几个新的连接过来,有新的连接会创建几个新的socket即cfd1,cfd2
接收完完连接后,服务器端通过epoll_create创建epoll对象efd
紧接着调用epoll_ctl,通过EPOLL_CTL_ADD将新连接放入epoll对象中去,让epoll对象管理起来
最后调用epoll_wait来监听所有由epoll对象管理的连接
以上就是一个epoll的极简模型,可以看到epoll主要就在于epoll_create,epoll_ctl,epoll_wait三个函数
accept创建socket
accept系统调用核心代码如下图
系统最终的调用会进入inet_csk_accept函数中,这也是最核心的逻辑。
inet_csk_accept函数主要作用就是将全连接队列中存放的socket取出来
拿出来之后,这个新的socket,由于socket是分struct socket和struct sock两大部分,拿出来后和用户进程组织就会形成
epoll实现
首先来看epoll_create函数
最终调用会创建一个struct eventepoll
,接下来我们看看struct eventepoll的结构,如下图
其中wait_queue_head_t wq这个等待队列和03、同步阻塞内核实现原理中的等待队列类似,其中存储的是进程
而struct list_head rdllisr中表示就绪描述符队列,在这个里面存储的就是epoll对象监听的连接,即accept取出的连接
最后还有一个struct rb_root rbr,这是为每个epoll对象做的一个红黑树,树上节点放置的就是一个连接对象
接下来我们再来看epoll_ctl(add,...)这一步,添加socket连接对象
这一步我们知道就是向epoll对象中添加连接对象
添加后,会将连接对象挂到struct rb_root rbr这个对应epoll对象的红黑树上去,红黑树节点名为epitem,用以关联epoll对象和socket对象
其中最主要的就是ep_poll_callback这个函数
最后是epoll_wait这一步
这一步会陷入内核态执行
陷入内核要做的第一件事情就是查看就绪队列中是否有就绪的socket,如果有,就立马将socket取走返回,不会阻塞进程。用户程序视角就是用户请求到达,需要处理
就绪队列若为空(epoll对象假设管理了1000个连接对象,但是这1000个链接对象没有一个有数据到达),就会定义等待队列,告诉内核epoll对象进程要陷入睡眠状态,如果后续有某个连接的数据到达,再唤醒。做完睡眠工作后,让出CPU
数据到达
当某个epoll对象的某个连接的数据达到后,会进行怎样的处理?
数据到达后的第一步与同步阻塞的过程十分类似,都是先将数据保存到socket接受队列
但是随后的处理就有不一样的地方
在将数据保存到socket的接收队列后,就会调用回调函数,这个函数是ep_poll_callback
这个函数就会将对应数据的连接添加到epoll对象的就绪队列中去
然后再去看等待队列中是否有对应的阻塞的进程。有就唤醒,没有,放到就绪队列中,不做其它事情
总结一下,如图
首先epoll_create创建一个epoll对象出来,主要关注三个结构,就绪队列,等待队列,红黑树
然后epoll_ctl添加socket连接,将其添加到红黑树的节点上去,红黑树节点名字叫做epitem
最后epoll_wait检查就绪队列,有就拿出来处理,没有就将自己阻塞放到等待队列上去,进入睡眠状态
数据包到达网卡后,会通过软中断,将数据包放到socket接受队列中去,并将自己插入epoll就绪队列中,同时检查是否有进程阻塞在epoll的等待中,有,则唤醒
这里与上一章节03、同步阻塞内核实现原理有些类似,但是又有些区别
同步阻塞是等待一个socket的连接请求,如果没有就陷入阻塞状态
但是epoll是多路复用,同时监听多个连接请求的到达,只要有一个到达就不会阻塞
二、epoll多路复用高性能的核心原因
epoll多路复用,其中多路指的是同时监听很多个tcp连接,复用指的是使用同一个进程。
由于是监听的多个链接,只要有一个连接有数据,那么这个进程就不会陷入阻塞状态,就会一直处理,相比同步阻塞就不会频繁陷入一个进程切换的场景,从而相比同步阻塞大大的提高了效率。
另外还有一个原因是在硬件上的
我们的CPU主要从L1,L2,L3缓存中提取数据,当一个进程长时间保持的时候,CPU需要的数据极大可能就在这3个缓存中,无需再从内存中去频繁提取数据,直接从3级缓存中取数据,速度高。简而言之就是提高了CPU的缓存命中率
参考拓展阅读
深入理解Linu网络