当前位置: 首页 > 科技观察

一篇了解Select、Poll和Epoll区别的文章

时间:2023-03-14 23:01:55 科技观察

1selectselect本质上是通过设置或检查存储fd标志的数据结构来进行下一步。这样带来的弊端是:单个进程能够监听的fd数量是有限的,也就是能够监听的端口数量是有限的。单个进程可以打开的最大连接数由FD_SETSIZE宏定义,它的大小是32个整数的大小(在32位以上的机器上,大小是3232,同理FD_SETSIZE在64位机器上是3264-bitmachine),当然我们可以修改,然后重新编译内核,但是可能会影响性能,需要进一步测试。一般这个数量和系统内存有很大关系,具体数量可以通过cat/proc/sys/fs/file-max查看。32位机器默认1024,64位机器默认2048。socket是线性扫描,也就是轮询,效率低:只知道发生了I/O事件,不知道是哪个streams,只不加区别地轮询所有的stream,找出可以读写数据的stream来操作。同时处理的流越多,不加选择的轮询时间就越长-O(n)。当socket比较多时,每次选择都要遍历FD_SETSIZE个socket,不管它们是不是active,这样会浪费大量的CPU时间。如果可以为socket注册一定的回调函数,当它们处于活动状态时,会自动完成相关操作,避免轮询。这就是epoll和kqueue。调用过程的缺点内核需要将消息传递给用户空间,需要内核拷贝动作。需要维护一个用于存放大量fds的数据结构,使得传递该结构时用户空间和内核空间的拷贝开销较大。每次调用select,都需要将fd集合从用户态复制到内核态。当fd很多的时候这个开销会很大。同时,每次调用select时,都需要遍历内核中传入的所有fds。当fd很多的时候这个开销也很大。select支持的文件描述符数量太少,默认10242个poll。poll的实现和select很相似,只是描述fd集合的方式不同。poll使用pollfd结构代替select的fd_set结构。其他类似,多管理一个描述符,也会根据描述符的状态轮询处理,但是poll对文件描述符的最大个数没有限制。poll和select同样的缺点是,包含大量文件描述符的数组是作为一个整体在用户态和内核地址空间之间复制的,不管这些文件描述符是否就绪,其开销随着数量的增加而增加文件描述符同时线性增加。它将用户传入的数组复制到内核空间,然后查询每个fd对应的设备状态:如果设备就绪,则在设备等待队列中添加一项继续遍历如果遍历所有后没有找到就绪设备fds,它挂起当前进程,直到设备就绪或者active超时,被唤醒后,再次遍历fd。这个过程会经历多次无意义的遍历。没有最大连接数限制,因为它是基于链表存储的。大量的fd数组在用户态和内核地址空间之间作为一个整体进行复制,不管是否有意义。如果fd上报后没有处理,会在下次poll的时候再上报fd3epoll可以理解为事件轮询,epoll会通知我们在哪个流上发生了哪个I/O事件。所以epoll是事件驱动的(每个事件都关联了fd),这时候我们对这些流的操作就有意义了。复杂度也降低到O(1)。3.1EPOLLLT和EPOLLET有两种触发模式:LT,默认模式(水平触发)只要fd还有数据要读,每次epoll_wait都会返回它的事件提醒用户程序操作,ET是“高速触发”"模式(edgeTrigger)只会提示一次,直到下一次数据流入时才会再次提示,不管fd中是否还有数据可读。因此,在ET模式下,读取一个fd时,必须将其buffer读完整,即读取返回值小于请求值或遇到EAGAIN错误。epoll使用“事件”就绪通知方式,通过epoll_ctl注册fd。一旦fd就绪,内核会使用类似的回调机制来激活fd,epoll_wait会收到通知。3.2优点最大并发连接没有限制,开启FD上限远大于1024(1G内存可以监控10万个端口左右)。提高效率,不是轮询,效率不会随着FD数量的增加而降低。只有活跃可用的FD才会调用回调函数。epoll最大的优点是它只关心“活跃”的连接数,与连接总数无关。所以在实际的网络环境中,epoll的效率会比select高很多。以及轮询内存拷贝,使用mmap()文件映射内存来加速与内核空间的消息传递;也就是说,epoll使用mmap来减少复制开销。epoll是通过内核空间和用户空间共享一块内存来实现的。从表面上看,epoll的性能最好,但是当连接数很少,连接非常活跃时,select和poll的性能可能会比epoll更好。毕竟epoll的通知机制需要大量的函数回调。epoll和select都可以提供多种I/O多路复用方案。它可以在当前的Linux内核中得到支持。其中epoll是Linux特有的,select应该是POSIX规定的,在一般的操作系统中都有实现。4小结select、poll、epoll都是IO多路复用机制,即可以监听多个描述符,一旦一个描述符就绪(readorwrite),就可以通知程序执行相应的读写操作。但是select、poll、epoll本质上都是同步I/O,因为它们都需要在读写事件就绪后负责读写,也就是说读写的过程是阻塞的,而异步I/O不需要自己负责读写,异步I/O的实现会负责将数据从内核空间拷贝到用户空间。参考Linux下select/poll/epoll机制的比较select、poll、epoll的区别总结[编辑]