暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

IO的多路复用之select、poll、epoll

匠工精神 2020-03-22
240

电商时代,高并发成了一个技术人经常挂在嘴边的专业名词,甚至业务人员都会好奇的来询问你,我们最近要做一档活动,系统能支撑的流量是多大?今天就简单的整理了下项目当中经常用到的nginx,redis等这些能够支撑高并发的铜墙铁壁。开始之前先留个悬念:nginx是master和woker进程工作模式,而redis是单线程的,是什么原因让它们能够高并发的处理请求?


额外知识点:

一、站在用户程序的角度上来讲,用户程序调用了“读”的系统调用后,进行的操作有两步:

  1、等待读取的数据就绪;

  2、将数据从系统内核内存区域拷贝到用户程序的内存区域。

二、对于Socket编程来说:

     第一步一般是等待数据从网络上传到本地,由内核程序存入内核的内存区域;   

     第二步就是用户程序从内核内存区将数据拷贝至自己的内存数据区。

如下图是个阻塞的IO:



解释此图:一个进程调用 recvfrom ,然后系统调用并不返回知道有数据报到达本地系统,然后系统将数据拷贝到进程的缓存中。 (如果系统调用收到一个中断信号,则它的调用会被中断)我们称这个进程在调用recvfrom一直到从recvfrom返回这段时间是阻塞的。当recvfrom正常返回时,我们的进程继续它的操作。


非阻塞IO:



非阻塞IO就是用户进程调用IO系统调用,如果被读取的数据没有准备好,则立即返回,进程不阻塞,继续执行下面的代码。所以非阻塞IO为了确保可以IO到数据,一般会采用轮询的方式,不断进行非阻塞IO的系统调用,直到数据就绪,再由操作系统执行完IO数据拷贝过程,然后正常返回。


接下来就进入本篇的正题,所谓IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。本质上是将访问数据是否就绪的函数与读取数据的函数分开。


与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程
,也不必维护这些进程/线程,从而大大减小了系统的开销。

目前支持I/O多路复用的系统调用主要有 select,poll,epoll
,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符(在linux世界里,一切皆文件,后续会有文章详细介绍),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作
但select,poll,epoll本质上都是同步I/O
,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间(今天不会涉及到异步IO,有兴趣的可以网上搜索资料)。


select方式:


调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。


 多路复用的高级之处在于:它能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。如下图:(此图来自网络)



select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理
。这样所带来的缺点是:

  1. select最大的缺陷就是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。


  2. 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。

    当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll做的


  3. 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。


POLL方式:



poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。



poll没有最大连接数的限制,但同样存在select的问题。



EPOLL方式:




Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用。是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。



epoll的优点

1. 支持一个进程打开大数 目的socket描述符(FD)

2. IO 效率不随FD数目增加而线性下降

3. 使用mmap加速内核 与用户空间的消息传递



epoll的工作模式:


LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。



ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述 符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致 了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。



epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用。



到此IO的多路复用知识基本结束了,有兴趣的还可以了解下零copy原理。下节会通过具体的demo来一步一步演示nginx/redis是如何调用系统的epoll方法的。内容提示通过strace、man等命令演示系统调用。敬请期待!









文章转载自匠工精神,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论