面试题答案
一键面试性能方面
- select:每次调用都需要将全部文件描述符集合从用户态拷贝到内核态,并且在内核检测完事件后,还需要将整个集合再从内核态拷贝回用户态,开销较大。同时,select采用线性扫描的方式遍历所有文件描述符,时间复杂度为O(n),随着文件描述符数量的增加,性能急剧下降。
- poll:与select类似,每次调用同样需要进行大量数据的拷贝,并且其检测事件的方式也是线性遍历,时间复杂度同样为O(n),在文件描述符较多时性能不佳。
- epoll:采用基于事件驱动的方式,内核在有事件发生时,通过回调函数将事件添加到就绪链表中。用户态应用通过epoll_wait调用获取就绪事件,只需要从内核态拷贝少量的就绪事件数据到用户态,大大减少了数据拷贝的开销。而且epoll_wait采用时间复杂度为O(1)的算法,无论文件描述符数量多少,其性能都较为稳定。
连接数处理方面
- select:受限于FD_SETSIZE宏定义,通常这个值比较小(例如1024),这就限制了它能同时处理的文件描述符数量,难以满足高并发场景下大量连接的需求。
- poll:理论上没有连接数的限制,因为它采用链表来存储文件描述符,但由于其线性扫描的检测方式,当连接数过多时性能会急剧下降,实际应用中也难以处理大量连接。
- epoll:能轻松处理大量连接,它在内核中维护了一个红黑树来管理文件描述符,这使得添加、删除和查找文件描述符的时间复杂度都为O(log n),所以可以高效地管理大量连接。
事件通知方式方面
- select:通过遍历所有文件描述符来判断哪些文件描述符有事件发生,应用程序需要在返回后自己去遍历检查哪些描述符处于就绪状态,无法得知具体发生了什么事件。
- poll:与select类似,也是通过遍历检查文件描述符集合来确定就绪状态,同样无法直接获取具体事件类型。
- epoll:采用事件通知机制,当有事件发生时,内核会将发生事件的文件描述符和对应的事件类型通过epoll_wait返回给应用程序,应用程序可以直接根据返回的事件信息进行处理,大大提高了处理效率。