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的更多相关文章

  1. 安卓输入子系统之inotify与epoll机制【学习笔记】【原创】

    平台信息:内核:linux3.1.0系统:android5.0平台:tiny4412 作者:庄泽彬(欢迎转载,请注明作者) 说明: 韦老师的安卓视频学习笔记 一.在安卓的输入子系统中如何监听文件的产生 ...

  2. 从select机制谈到epoll机制

    目录 为什么要用select机制 等待队列 唤醒操作 什么是select机制 关于fd_set select使用 poll函数 为什么select效率较低 什么是epoll epoll机制实现思路 e ...

  3. 【Unix环境编程】select、poll、epoll机制的联系与区别

    在linux设计并发网络程序,主要有如下几种模型:Apache模型(Process Per Connection, PPC).TPC(Thread Per Connection)模型,select机制 ...

  4. 基于python的opcode优化和模块按需加载机制研究(学习与个人思路)(原创)

    基于python的opcode优化和模块按需加载机制研究(学习与思考) 姓名:XXX 学校信息:XXX 主用编程语言:python3.5 个人技术博客:http://www.cnblogs.com/M ...

  5. 剖析epoll机制

    剖析epoll机制 Linux epoll机制; 写这篇文章的原因是, 上次百度面试被问到一个事件怎么添加到epoll的双向链表中的; 这个问题比较深入, 涉及到内核的实现问题, 今天就来理解一下; ...

  6. select、poll和epoll机制

    一.参考网址 1.select函数及fd_set介绍 2.linux select 函数和 fd_set 用法 2.select.poll和epoll的区别 3.利用select实现IO多路复用TCP ...

  7. Find security bugs学习笔记V1.0

    Find security bugs学习笔记V1.0 http://www.docin.com/p-779309481.html

  8. PHP代码安全学习笔记V1.0

    PHP代码安全学习笔记V1.0http://www.docin.com/p-778369487.html

  9. Java安全防御学习笔记V1.0

    Java安全防御学习笔记V1.0http://www.docin.com/p-766808938.html

随机推荐

  1. Tomcat集群Cluster实现原理

    1.Tomcat集群         Tomcat集群的问题之一是如何处理Session,Session是有状态的,请求到了Tomcat,后续流传是要根据上下文(Context)来进行的.我们可以改造 ...

  2. golang redis

    安装 下载第三方包: go get -u github.com/go-redis/redis/v9 连接 // 定义一个rdis客户端 var redisdb *redis.Client // 初始化 ...

  3. openresty 自动 deploy github repository

    配置 deploy key 配置 webhooks https://gist.github.com/hangj/ce6aabac77e96b010e3b361e18422013

  4. .Net Core配置Configuration源码研究

    最近又研究了一下.NetCore配置选项的源码实现,又学习到了不少东西.这篇文章先写一下IConfiguration的学习成果,Options的后面补上 核心类 ConfigurationBuilde ...

  5. 剑指offer计划9(动态规划中等版)---java

    1.1.题目1 剑指 Offer 42. 连续子数组的最大和 1.2.解法 得到转移方程后,单次遍历. 当前面的连续子数组的和比较是否大于0,是则加起来, 若小于零,则当前的值就可当子数组的开头. 判 ...

  6. 云效x钉钉:让研发工作更简单

    云效x钉钉:让研发工作更简单,奔走相告,云效&钉钉集成实现组织架构.成员同步以及消息通知啦! 我们知道云效致力于智能化.安全可追溯.高效.简单.灵活,「云效新一代企业级DevOps平台」阿里云 ...

  7. jq的slideToggle效果

    slideToggle() 方法通过使用滑动效果(高度变化)来切换元素的可见状态. 如果被选元素是可见的,则隐藏这些元素,如果被选元素是隐藏的,则显示这些元素. 例子:一个简单的下拉菜单效果----& ...

  8. 代码注释规范之Doxygen

    1.Doxygen简介 Doxygen是一个程序的文档产生工具,可以将程序中的注释转换成说明文档或者说是API参考手册,从而减少程序员整理文档的时间.当然这里程序中的注释需要遵循一定的规则书写,才能让 ...

  9. Python - 3.8 新特性之仅位置参数 & 仅关键字参数

    前置知识 Python 函数:https://www.cnblogs.com/poloyy/p/15092393.html 什么是仅限位置形参 仅限位置形参是 Python 3.8 才有的新特性 新增 ...

  10. MySQL数据库初体验

    一.数据库的基本概念1.数据(Data) 描述事物的符号记录 包括数字,文字,图形,图像,声音,档案记录等 以"记录"形式按统一的格式进行存储 2.表 将不同的记录组织在一起 用来 ...