什么是epoll?

背景:

最近学习了linux,提到了epoll多路复用技术,不太了解,特地学习做个笔记。

讲epoll之前,先说下,几种IO的方式。

一、BIO

同步阻塞模式,在jdk早期,我们在建立网络连接时,采用的就是BIO模式,原理就是服务端启动一个socket,会有很多客户端连接,每建立一个连接,都会有一个线程,询问服务端是否能有线程相应,如果没有,该连接都会进入阻塞状态,直到轮到自己。说的直观点,往一堆池子里蓄水,一个池子蓄水,需要从开始到到蓄满,才能到下一个池子。BIO适用于连接数比较少,简单的架构,程序直观易理解

二、NIO

同步非阻塞模式,NIO一个重要的地方在于,当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上,这在无形当中就降低了建立多个线程带来的资源消耗,多路复用器会去轮询这些连接,当有请求时,就会建立一个线程处理。说的直观点,往一堆池子里蓄水,不需要一个一个处理完成,可以同时往所有池子里蓄水,通过轮询的方式来确定哪个池子水蓄满了。当然NIO同样会有缺点,就是当并发请求变高,同样会出现BIO一样的问题。NIO适用于连接数较多,但操作较轻的架构,比如即时通讯服务。

三、AIO

异步非阻塞模式,与上面两种模式不同,AIO实现的是一个有效请求一个线程,客户端的IO请求都是由OS先处理再通知服务器去启动一个线程处理。说的直观点,往一堆池子里同时蓄水,不需要时时刻刻轮询检查,在池子里装一个传感器,当池子水蓄满时,就进行通知。AIO适用于连接数多,且操作重的架构,比如视频服务器。

下面重点讲IO复用技术,目前常用的IO复用模型有三种:select,poll,epoll

1.select 

select的表现在于知道有事件发生,但需要通过轮询去确认是哪一个事件,它将用户态的fd都复制到内核态空间,查询每个fd的状态,它的时间复杂度就是O(n),因为需要无差别轮询,所以流越多,轮询时间越长。

2.poll

poll的表现与select基本一致,也是复制 ——>轮询的动作,不同点在于它没有最大连接数的限制,因为它的存储是基于链表,它的时间复杂度也是O(n)

3.epoll

epoll不同于select和poll的轮询,epoll会将哪个流发生什么事件通知我们,所以说epoll是事件驱动,所以,对具体的流操作都是有意义的,它的时间复杂度是O(1)

select,poll,epoll三者都是IO多路复用的机制,表现在于通过一种机制,监控多个文件描述符,一旦某个描述符就绪(读写),能够通知程序进行相应的操作,那么三种的特点是怎样的呢?

select:因为我们操作系统涉及用户态跟内核态,每次调用select,都需要把fd集合从用户态拷贝到内核态,这个复制开销在fd很多时会很影响性能;每次调用select都需要轮询遍历所有fd,这个开销在fd多时也会很影响性能;需要一个来存储大量fd的数据结构,这无形中会使得用户空间和内核空间在传递数据时开销变大

poll:poll的机制本质上跟select没有差别,不同点在于,没有最大连接数的限制,因为它的数据结构基于链表,那么它同样会引发一个问题,就是当不加限制的复制,会有很多无效fd产生,并且poll的水平触发,报告了该fd,没有处理,下次还会再次报告该fd

epoll:

epoll有两种模式触发,LT跟ET,LT表现的是只要这个fd还有数据可读,每次epoll的wait都会返回它的时间,提醒程序去操作,ET,它只会触发一次,直到下次再有数据流入之前都不会再有提示,所以ET模式下,一定要buffer读完,epoll采用事件通知方式,epoll_ctl注册fd,一旦fd就绪,内核采用callback回调的方式激活fd,epoll_wait就可以收到通知,特点就在于不会充斥大量无效fd。优点:没有最大并发连接的限制,FD上限很高;效率高,非轮询方式,采用事件回调形式,只会操作有效fd;内存拷贝,不同于select跟poll,epoll在概念上用户态跟内核态有一款共享空间,利用mmap()文件映射,打打减少复制开销。

epoll提供了三个函数,create,ctl,wait,create是创建一个句柄,ctl是注册要监听的fd,wait是等待事件的产生。下面是具体过程:

1.epoll_create创建一个句柄,之后所有的使用都靠这个句柄来表示

2.epoll_ctl通过此调用向epoll对象中添加、删除、修改等事件类型

3.epoll_wait收集监控就绪的事件

最后说一下,epoll的epoll存储是基于红黑树链表实现的,epoll结构体中有两个重要属性,一个是rb_root rbr,红黑树的根节点,这棵树存储着所有添加到epoll的需要监听的事件,另一个是list_head rdList,表示的是需要epoll_wait返回给用户的事件,每个epoll对象都有一个独立的结构体,用于存放epoll_ctl向对象中添加进来的事件,这些事件都会挂载在红黑树中,重复添加的事件就会被识别出来,所有的epoll事件都会跟设备驱动建立回调关系,它会将事件添加到rdlist中,每一个机构体中,都会有一个事件结构体。

红黑树的事件复杂度是lgn,效率很高


版权声明:本文为weixin_44446046原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。