一、I/O复用的介绍
- I/O复用是一种能够同时处理多个I/O操作的技术,适用于高并发场景。
-
使用场景
- select/poll:适用于连接数较少的场景。
- epoll:适用于大规模的网络服务,如Web服务器。
二、实现方式
1.select
select
是一种系统调用,用于监视多个文件描述符的状态。- 使用方式:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- timeout参数用于设置超时时间,超时后返回0。
select的缺点
- 最大文件描述符数量限制(一般为1024)。
- 返回的文件描述符集合需要遍历,时间复杂度为O(n)。
2.poll
poll的基本概念
- poll与select类似,是一种用于监控多个文件描述符的I/O方法。
- 使用方式:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- poll的缺点:
- 需要遍历所有监视的文件描述符,时间复杂度为O(n)。
3. epoll
epoll的基本概念
- epoll是Linux特有的I/O复用方法,优化了select和poll的性能。
- 使用方式:
epoll_create
创建一个epoll实例。epoll_ctl
用于添加、修改或删除文件描述符。epoll_wait
用于等待事件的发生。
epoll的优点
- 支持大规模并发,时间复杂度为O(1)。
- 可以同时监视多个文件描述符,适合高并发场景。
三、select、poll和epoll的区别
1.描述符上限:select 的文件描述符数量通常有限制(一般为1024),而 poll 和 epoll 对文件描述符的数量没有限制。
2.数据结构传递方式:select 在每次调用时需要将所有注册的文件描述符集合复制到内核中;poll 则使用一个 pollfd 数组来传递数据,仍需在每次调用时进行复制;相比之下,epoll 维护内核中的红黑树和双向链表,仅在有事件发生时进行处理,避免了不必要的复制。
3.性能开销:select 因为需要多次进行内核态切换,表现出较高的性能开销;poll 的性能开销稍微较低,但仍需切换用户态与内核态;而 epoll 的开销最低,仅在事件发生时切换,能显著减少不必要的性能损耗。
3.时间复杂度:在 select 和 poll 中,处理的复杂度都是 O(n),需要遍历所有注册的文件描述符;而在 epoll 中,复杂度则降低为 O(K),仅需要遍历就绪事件的数量,提升了效率。 适用场景:select 和 poll 适合处理较少的文件描述符,而 epoll 更适合处理大量并发连接,能够提供更好的性能。
4.支持边缘触发:epoll 支持边缘触发模式,而 select 和 poll 则不支持这种机制。
5.返回就绪事件的数量:poll 返回就绪的文件描述符数量,epoll 通过回调机制或事件循环返回就绪事件,相比之下更灵活高效。