epoll机制
wrk用非阻塞多路复用IO技术创造出大量的连接,从而达到很好的压力测试效果。epoll就是实现IO多路复用的关键。
本节是对epoll的本质的学习总结,进一步的参考资料为:
《深入理解Nginx:模块开发与架构解析(第二版)》,陶辉
首先分析网络数据接收模型。
计算机分为硬件中断和软件中断,硬件中断是由外接设备产生的,比如网卡,键盘,鼠标等这些都是硬件设备。硬件设备向CPU发出中断信号,高电平信号到达CPU引脚,触发CPU立即执行中断。软中断就是由程序产生的中断。
当网卡收到外部网络发送过来的数据,网卡会做相应的处理,然后网卡发送数据到计算机内存中,之后向CPU发出硬件中断信号。CPU得到信号后立即中断当前任务,去处理网络数据,将内存中的网络数据写入socket对象中,同时唤醒等待该数据的进程。
socket对象用于收发网络数据,socket对象由进程创建后被文件描述符指向,即fd指针。socket对象指定了“端口号”,而网络数据包里包含了端口号,这使得CPU可以准确将数据写入对应的socket中。socket对象里有三个数据结构: 发送缓冲区,接收缓冲区和等待队列。接收缓冲区就是负责接收内存中的数据,并且等待被进程处理。等待队列实际上是指针,该指针指向进程时,表示进程处于等待状态,于是CPU不会处理该进程,而是处理其他进程,直到该进程被中断程序唤醒,同时中断程序移除被监听的socket上的等待队列,这样该进程重新加入进程的运行状态,被CPU处理,这样,进程拿到了socket缓冲区中的数据,recv这一环顺利通过,可以执行下一步。
在早期,互联网用户少,因此一台服务器每当被一个客户端连接,就建立一个进程,该进程只监听一个socket, 服务器能够承受住负载。当用户越来越多时,一台服务器仍然起大量进程已不现实。因此一个进程监听多个连接的技术应运而生,这就是IO多路复用技术。
最早的IO多路复用技术的思路较为简单,这就是select方法。
进程创建并监听多个socket对象,这些socket对象的描述符被写到数组fds中,进程执行系统调用select时,操作系统将进程放入每个socket的等待队列,此时进程被阻塞。其中任意一个socket被写入数据(实际上,唤醒工作是中断程序做的),进程就会被唤醒,并遍历fds中的socket对象,并读取缓冲数据,从而继续执行下去,此时进程处于运行态。
select方法让一个进程等待再唤醒执行它的过程中,一共有3次遍历,2次内核传递。让进程处于等待状态时,等待即阻塞,因为CPU执行其他进程去了,所以等待状态下的进程不消耗CPU资源,该进程会被操作系统放入被监听的所有的socket的等待队列中,因此需要遍历fds,遍历之前需要把fds整个列表传递到内核去。等到设备接收到网络数据,进程被唤醒的时候,操作系统要将fds中每个socket的等待队列中的进程指针清空,因此再一次遍历,遍历前仍然要传递fds到内核。涉及到进程的操作必然由内核执行,进程内部的执行则是用户空间权限,不需要内存干涉。最后一次遍历,是进程遍历fds上的socket(fds本来就在用户态),直到找到有缓冲数据的。
这样会带来两个问题,1.频繁的内核传递,2.频繁的遍历。问题的根源在于,进程的每一次状态更新就要重新传递fds以及遍历(fds的状态更新)。传递fds的原因显而易见,每一次调用select都是一次独立的监听一群socket的行为,在实际场景下,fds中的socket并不会较大规模地变化,因此fds最好整个列表只传一次,如果有修改,也只是对整个小增小减。遍历既源于fds传递至内核后要让fds中的每个socket和进程建立联系,也源于进程唤醒后要寻找到有缓冲数据的socket, 所以最好能进程和fds一次建立联系,然后进程能一次就找到需要的socket.
fds的状态变化和进程状态的变化是一起发生的,能不能让它们分开发生?即进程的状态变化不需要和fds重新建立连接?此外,进程也不知道fds哪个socket发生了变化,因为fds不存储发生变化的信息。进程既然要和fds每个socket发生关系,为什么fds不派一个管理者代表来和进程沟通呢?这个管理者,就是event poll。
epoll就是在fds的基础上,增加了一个eventpoll数据结构,进程创建fds之后,其中的socket都为空时(如果不为空,recv直接拿到socket数据,就不阻塞了),进入阻塞状态,此时fds列表整体传入内核。所有socket与eventpoll对象建立关系,即将eventpoll对象放入所有socket的等待列表里。然后eventpoll对象的等待列表中放入进程。这是epoll方法下的进程阻塞模型,eventpoll不会频繁地改变状态,所以fds列表只传一次。eventpoll还维护一个rdlist数组,当多个socket收到数据,内核中断程序拿到了网络数据包中的五元组信息,拿到了端口号,找到了socket对象,同时知道了socket对象的地址,于是在rdlist数组中写入这些socket对象的地址。进程被唤醒时,被从eventpoll的等待列表里移除,进程又读取rdlist中的socket对象地址,直接找到收到数据的socket.
epoll的核心在于操作eventpoll管理进程状态改变,只要传递一次fds,遍历1次fds就可以阻塞进程,唤醒进程则只需操作一次eventpoll。极大降低了开销。epoll的根本原理还是中间层原理。
参考
注1:等待队列的真正意思是,该socket有个列表,里面存储了所有监听该socket的进程的fd描述符。所以,可以有多个进程监听同一个端口。
注2:网卡将数据写入内存,中断程序将内存中的数据写入socket对象中。唤起进程的是中断程序,中断程序是硬中断发起后,被CPU执行的。唤起进程的同时,将所有等待队列清空,清空后便可以CPU执行该进程,执行中,遍历socket,如果哪个socket收到了数据,便处理哪一个recv. select,就是选择,就是遍历式地选择。
注3:进程是被内核管理的,所以,操作进程,就必须将所涉及到的数据传递给内核。内核和应用空间的关系,理解成包围和被包围的关系更为合适。
其他小问题
什么是事件?
事件是被进程所等待的数据。1个事件可以让多个进程等待。
为什么说等待了,就会阻塞呢?
因为进程A创建完socket之后,下一步到了recv方法,此时进程A被丢入(其实是生成一个等待中的引用)socket对象的等待队列中去(内存),CPU就去执行其他的进程了。直到有socket事件被硬中断传入,CPU将其写入内存,进程A才再次被唤醒。
为什么阻塞是进程调度关键的一环?
阻塞又叫做等待状态,等待什么? 进程在等待某一个事件的发生,在等待时,无法进入下一步状态。对于处理网络的进程,就是等待接收网络数据包。
eventpoll对象的数据结构是怎样的?
eventpoll用到了红黑树。就绪列表需要快速地被加入和删除,所以,就绪列表是红黑树。
为什么select监视的最大socket数量是1024个?
因为select在每次进程状态改变时候,要3次遍历fds列表,2次将fds列表传递到内核,fds列表变大,即提升了遍历时间,又因为复制更大的数据传递至内核,用户空间到内核空间的复制传输开销较大。所以限制了fds的大小。默认最大是1024.
对epoll机制的学习理解v1的更多相关文章
-
安卓输入子系统之inotify与epoll机制【学习笔记】【原创】
平台信息:内核:linux3.1.0系统:android5.0平台:tiny4412 作者:庄泽彬(欢迎转载,请注明作者) 说明: 韦老师的安卓视频学习笔记 一.在安卓的输入子系统中如何监听文件的产生 ...
-
从select机制谈到epoll机制
目录 为什么要用select机制 等待队列 唤醒操作 什么是select机制 关于fd_set select使用 poll函数 为什么select效率较低 什么是epoll epoll机制实现思路 e ...
-
【Unix环境编程】select、poll、epoll机制的联系与区别
在linux设计并发网络程序,主要有如下几种模型:Apache模型(Process Per Connection, PPC).TPC(Thread Per Connection)模型,select机制 ...
-
基于python的opcode优化和模块按需加载机制研究(学习与个人思路)(原创)
基于python的opcode优化和模块按需加载机制研究(学习与思考) 姓名:XXX 学校信息:XXX 主用编程语言:python3.5 个人技术博客:http://www.cnblogs.com/M ...
-
剖析epoll机制
剖析epoll机制 Linux epoll机制; 写这篇文章的原因是, 上次百度面试被问到一个事件怎么添加到epoll的双向链表中的; 这个问题比较深入, 涉及到内核的实现问题, 今天就来理解一下; ...
-
select、poll和epoll机制
一.参考网址 1.select函数及fd_set介绍 2.linux select 函数和 fd_set 用法 2.select.poll和epoll的区别 3.利用select实现IO多路复用TCP ...
-
Find security bugs学习笔记V1.0
Find security bugs学习笔记V1.0 http://www.docin.com/p-779309481.html
-
PHP代码安全学习笔记V1.0
PHP代码安全学习笔记V1.0http://www.docin.com/p-778369487.html
-
Java安全防御学习笔记V1.0
Java安全防御学习笔记V1.0http://www.docin.com/p-766808938.html
随机推荐
-
THP Transparent HugePages 相关知识与关闭
近期遇到个LINUX系统内存比較大.未开 HugePages,业务有变化导致ORACLE连接数剧增至上千个,PageTables达到上百G.导致内存不足系统HANG住的案例. 因此须要开启 HugeP ...
-
PHP语言基础06 MySql By ACReaper
上篇介绍了如用PHP连接上MySql进行,并进行sql语句的执行.但是我们没有介绍,如何输出处理的结果,如何获得处理的结果. 这里要先说明Mysql有两种查询处理模式,一种是有缓冲的查询处理模式,一种 ...
-
x86_64的内存映射
对于x86_64来说,逻辑地址由16位选择子和64位偏移量组成(而32位时,逻辑地址由16位段选择符和32位偏移量组成),段寄存器仅仅存放选择子.CPU的分段单元(SU)执行以下操作:[1] 先检查选 ...
-
windows python flask上传文件出现IOError: [Errno 13] Permission denied: 'E:\\git\\test\\static\\uploads'的解决方法
在浏览器中输入时,出现IOError: [Errno 13] Permission denied: 'E:\\git\\test\\static\\uploads' http://127.0.0.1: ...
-
Mac下安装PEAR
The following instructions install PEAR and PECL on Mac OS X under/usr/local/. PECL is bundled with ...
-
spring中@Resource和@Autowired理解
一.@Resource的理解 @Resource在bean注入的时候使用,@Resource所属包其实不是spring,而是javax.annotation.Resource,只不过spring支持该 ...
-
springcloud第四步:ribbon搭建服务负载均衡
使用ribbon实现负载均衡 启动两个会员服务工程,端口号分别为8762.8763,订单服务 使用负载均衡策略轮训到会员服务接口. 什么是ribbon ribbon是一个负载均衡客户端 类似nginx ...
-
Nginx实践篇(2)- Nginx作为静态资源web服务 - 控制浏览器缓存、防盗链
一.控制浏览器缓存 1. 浏览器缓存简介 浏览器缓存遵循HTTP协议定义的缓存机制(如:Expires;Cache-control等). 当浏览器无缓存时,请求响应流程 当浏览器有缓存时,请求响应流程 ...
-
pip问题
“ModuleNotFoundError: No module named 'pip'”的解决方法:http://www.pianshen.com/article/476461202/ pip错误 I ...
-
oracle单行函数 之 字符函数
Upper(字符串 / 列):将输入的字符串变成大写 Lower(字符串 / 列):将输入的字符串变成小写 Initcap(字符串 / 列):开头首字母大写 Length(字符串 / 列):字符串长度 ...