select,poll,epoll

select、poll、epoll 区别总结:

底层实现

select/poll

首先把关注的Socket集合从用户态拷贝到内核态,然后由内核检测事件,遍历整个集合(由于线性结构实现,时间复杂度为O(n))找到对应的socket,并改变状态为可读或者可写,然后再拷贝整个socket集合到用户态,继续遍历整个集合找到可读或可写的socket对其处理。

epoll

通过两个手段解决了上面的不足

  1. 在内核中使用了红黑树来跟踪进程所有的待检测文件描述符(时间复杂度一般为O(logn)),减少了内核和用户空间大量的数据拷贝和内存分配。
  2. 使用了事件驱动的机制,内核里维护了一个链表来记录就绪事件。不需要轮询检测了。

epoll是Linux下高效的I/O多路复用机制之一,可以有效地处理大量的并发连接,提高网络应用程序的性能和吞吐量。epoll有三种工作模式:LT模式、ET模式和EPOLLONESHOT模式

  1. LT模式(Level Triggered,水平触发)

在LT模式下,当一个描述符上有可读或可写事件发生时,epoll_wait()函数会立即返回,应用程序可以在返回的events列表中处理这些事件。如果应用程序没有处理完所有的事件,下次调用epoll_wait()函数时会再次返回这些事件。

LT模式是epoll的默认模式,也是最常用的模式。它的优点是容易使用,可以直接处理每个事件,缺点是在高并发情况下,可能会导致频繁的epoll_wait()函数调用和事件处理,影响程序性能。

  1. ET模式(Edge Triggered,边缘触发)

在ET模式下,epoll_wait()函数只会在描述符上出现可读或可写事件时返回,而不是像LT模式一样每次调用都会返回所有的事件。当应用程序处理完这些事件后,必须立即读取或写入数据,直到返回EAGAIN错误,否则将无法再次接收到该描述符上的事件。

ET模式的优点是可以提高程序性能,减少epoll_wait()函数的调用次数。缺点是使用起来相对复杂,需要注意每个事件的处理方式和状态。

  1. EPOLLONESHOT模式

在EPOLLONESHOT模式下,epoll_wait()函数只会返回一次该描述符上的事件,之后该描述符将被从epoll队列中删除,直到应用程序重新将该描述符添加到epoll队列中。

EPOLLONESHOT模式的优点是可以避免多个线程同时处理同一个描述符上的事件,确保事件的顺序和正确性。缺点是在重新添加描述符到epoll队列时,可能会出现并发访问的问题,需要进行额外的同步措施。

总之,不同的epoll工作模式各有特点,需要根据应用程序的具体需求进行选择和使用。常见的使用方式是使用LT模式处理普通的网络应用程序,使用ET模式处理高并发的网络应用程序,使用EPOLLONESHOT模式处理一些特定的情况,例如单线程处理多个描述符的情况。

1、支持一个进程所能打开的最大连接数

select

单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

poll

poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

epoll

虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接

2、FD剧增后带来的IO效率问题

select

因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll

同上

epoll

因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3、 消息传递方式

select

内核需要将消息传递到用户空间,都需要内核拷贝动作

poll

同上

epoll

epoll通过内核和用户空间共享一块内存来实现的。(错的) epoll_wait 实现的内核代码中调用了 __put_user 函数,这个函数就是将数据从内核拷贝到用户空间。

应用场景

不是用 epoll 就可以了,select 和 poll 都还没有过时,都有各自的使用场景。

1. select 应用场景

select 的 timeout 参数精度为 1ns,而 poll 和 epoll 为 1ms,因此 select 更加适用于实时要求更高的场景,比如核反应堆的控制。

select 可移植性更好,几乎被所有主流平台所支持。

2. poll 应用场景

poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,应该使用 poll 而不是 select。

需要同时监控小于 1000 个描述符,就没有必要使用 epoll,因为这个应用场景下并不能体现 epoll 的优势。

需要监控的描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且epoll 的描述符存储在内核,不容易调试。

3. epoll 应用场景

只需要运行在 Linux 平台上,并且有非常大量的描述符需要同时轮询,而且这些连接最好是长连接。

------摘自pdai

同步VS异步   阻塞VS非阻塞

首先要明白IO包括且只包括两个流程:等待数据,读取(拷贝)数据

 

阻塞IO VS 非阻塞IO

应用程序所需的数据还没到达用户空间,就是阻塞IO,阻塞在了等待数据和拷贝数据上

用户程序不参与上面两个过程,数据被拷贝到用户空间后,才去通知用户程序,一上来就可以直接操作数据,没有因为IO发送阻塞,就是非阻塞IO

 

同步IO VS 同步阻塞IO

同步io一定是阻塞的,不可能是不阻塞的,因为同步意味着必须拿到数据才可进行之后的操作 

 

异步IO和异步阻塞/非阻塞IO

异步IO是指发起IO请求后,不用拿到IO的数据就可以继续执行。

异步阻塞IO:用户程序发起请求后,继续执行,拷贝数据过程中是阻塞的

异步非阻塞IO:用户程序发起请求后,继续执行,拷贝数据到用户空间的过程中用户程序也不会阻塞

 

posted @ 2022-07-17 18:57  山野村夫01  阅读(40)  评论(0编辑  收藏  举报