epoll、select 和 poll优缺点详细介绍
2025-3-10 diaba
IO多路复用是一种高效的并发处理技术,通过内核级别的支持,可以显著提高网络服务的性能和并发能力。常见的IO多路复用技术包括select、poll、epoll、kqueue和IOCP,每种技术都有其适用场景和优缺点。在实际应用中,可以根据具体需求选择合适的IO多路复用机制
epo
ll
、select
和 poll
都是用于实现 IO多路复用 的系统调用,它们允许程序同时监控多个文件描述符(如套接字、文件等),以确定哪些描述符已经准备好进行读写操作。这使得单个线程能够高效地管理多个并发连接,而无需为每个连接创建单独的线程或进程。
尽管它们的目标相同,但实现机制和性能特点有很大差异。以下是对这三种机制的详细介绍:
1. select
原理
select
是最早出现的IO多路复用系统调用,它通过维护三个文件描述符集合(fd_set
)来监控文件描述符的状态变化:
-
readfds
:监控可读的文件描述符。 -
writefds
:监控可写的文件描述符。 -
exceptfds
:监控发生异常的文件描述符。
调用 select
时,内核会检查这些集合中的所有文件描述符,返回已经准备好的描述符数量。程序可以通过遍历这些集合来找到具体的就绪描述符。
优点
- 简单易用:API简单,跨平台支持好(在Unix/Linux和Windows上都可用)。
- 无需额外库:直接使用标准库函数即可实现。
缺点
-
性能问题:
-
每次调用
select
都需要复制文件描述符集合,效率低。 - 需要遍历整个集合来找到具体的就绪描述符,时间复杂度为 O(n)。
-
每次调用
-
文件描述符数量限制:
fd_set
的大小通常受限(如1024),无法监控大量文件描述符。
示例代码
#include <stdio.h> #include <sys/select.h> #include <unistd.h> int main() {
fd_set readfds; struct timeval timeout; int maxfd; // 初始化文件描述符集合 FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds); // 监控标准输入 maxfd = STDIN_FILENO + 1; // 设置超时时间 timeout.tv_sec = 5;
timeout.tv_usec = 0; int result = select(maxfd, &readfds, NULL, NULL, &timeout); if (result == -1) {
perror("select");
} else if (result == 0) { printf("Timeout occurred! No data after 5 seconds.\n");
} else { if (FD_ISSET(STDIN_FILENO, &readfds)) { printf("Data is available now.\n"); // 读取数据 }
} return 0;
}
2. poll
原理
poll
通过一个 pollfd
结构数组来监控文件描述符的状态。每个 pollfd
结构包含一个文件描述符和其关注的事件类型(如可读、可写)。调用 poll
时,内核会检查数组中的所有文件描述符,返回已经准备好的描述符数量。
优点
-
无文件描述符数量限制:不像
select
,poll
不受文件描述符数量的限制。 - 跨平台支持:在Unix/Linux和Windows上都可用。
缺点
-
性能问题:
-
每次调用
poll
都需要遍历整个数组来找到具体的就绪描述符,时间复杂度为 O(n)。 - 无法感知文件描述符的动态变化,效率较低。
-
每次调用
示例代码
#include <stdio.h> #include <poll.h> #include <unistd.h> int main() { struct pollfd fds[1]; int ret;
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
ret = poll(fds, 1, 5000); // 5秒超时 if (ret == -1) {
perror("poll");
} else if (ret == 0) { printf("Timeout occurred! No data after 5 seconds.\n");
} else { if (fds[0].revents & POLLIN) { printf("Data is available now.\n"); // 读取数据 }
} return 0;
}
3. epoll
原理
epoll
是Linux特有的IO多路复用机制,通过内核维护一个事件表来监控文件描述符的状态变化。当有事件发生时,内核会直接通知应用程序,而无需像 select
或 poll
那样遍历所有文件描述符。
epoll
的主要操作包括:
-
epoll_create
:创建一个epoll
实例。 -
epoll_ctl
:向epoll
实例中添加、修改或删除文件描述符。 -
epoll_wait
:等待事件发生,返回就绪的文件描述符列表。
优点
-
高性能:
- 内核直接维护事件表,无需遍历文件描述符,时间复杂度为 O(1)。
- 支持大量文件描述符(理论上可达百万级)。
- 动态感知:可以动态添加或删除文件描述符,无需重新注册。
缺点
-
仅支持Linux:
epoll
是Linux特有的,不支持其他操作系统。 - 复杂性:API相对复杂,需要更多代码来实现。
示例代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> #define MAX_EVENTS 10 int main() { int epoll_fd, listen_fd, conn_fd; struct epoll_event event, events[MAX_EVENTS]; int nfds; // 创建监听socket listen_fd = socket(AF_INET, SOCK_STREAM, 0); // 绑定地址、监听等操作... // 创建epoll实例 epoll_fd = epoll_create1(0); if (epoll_fd == -1) {
perror("epoll_create"); exit(EXIT_FAILURE);
} // 将监听socket添加到epoll event.data.fd = listen_fd;
event.events = EPOLLIN | EPOLLET; // 边缘触发 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {
perror("epoll_ctl"); exit(EXIT_FAILURE);
} while (1) {
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for (int n = 0; n < nfds; ++n) { if (events[n].data.fd == listen_fd) { // 接收新连接 conn_fd = accept(listen_fd, NULL, NULL); // 将新连接添加到epoll event.data.fd = conn_fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event);
} else { // 处理客户端数据 char buffer[1024]; int bytes = read(events[n].data.fd, buffer, sizeof(buffer)); if (bytes > 0) { printf("Received data: %s\n", buffer);
} else {
close(events[n].data.fd);
}
}
}
}
close(listen_fd);
close(epoll_fd); return 0;
}
总结
-
select
:- 优点:简单易用,跨平台。
- 缺点:性能低,文件描述符数量受限。
- 适用场景:小规模并发(如文件描述符数量较少)。
-
poll
:- 优点:无文件描述符数量限制,跨平台。
- 缺点:性能低,需要遍历所有文件描述符。
- 适用场景:中等规模并发。
-
epoll
:- 优点:高性能,支持大量并发。
- 缺点:仅支持Linux,API复杂。
- 适用场景:大规模并发(如高并发网络服务)。
在实际开发中,可以根据具体需求选择合适的IO多路复用机制。对于高并发场景,epoll
是首选;对于跨平台需求,select
或 poll
可能更适合。
日历
个人资料

diaba 寻求合作请留言或联系mail: services@jiucaiyuan.net
链接
最新文章
存档
- 2025年3月(9)
- 2025年2月(20)
- 2025年1月(2)
- 2024年10月(1)
- 2024年8月(2)
- 2024年6月(4)
- 2024年5月(1)
- 2023年7月(1)
- 2022年10月(1)
- 2022年8月(1)
- 2022年6月(11)
- 2022年5月(6)
- 2022年4月(33)
- 2022年3月(26)
- 2021年3月(1)
- 2020年9月(2)
- 2018年8月(1)
- 2018年3月(1)
- 2017年3月(3)
- 2017年2月(6)
- 2016年12月(3)
- 2016年11月(2)
- 2016年10月(1)
- 2016年9月(3)
- 2016年8月(4)
- 2016年7月(3)
- 2016年6月(4)
- 2016年5月(7)
- 2016年4月(9)
- 2016年3月(4)
- 2016年2月(5)
- 2016年1月(17)
- 2015年12月(15)
- 2015年11月(12)
- 2015年10月(6)
- 2015年9月(11)
- 2015年8月(8)
分类
热门文章
- SpringMVC:Null ModelAndView returned to DispatcherServlet with name 'applicationContext': assuming HandlerAdapter completed request handling
- Mac-删除卸载GlobalProtect
- java.lang.SecurityException: JCE cannot authenticate the provider BC
- MyBatis-Improper inline parameter map format. Should be: #{propName,attr1=val1,attr2=val2}
- Idea之支持lombok编译
标签
最新评论
- logisqykyk
Javassist分析、编辑和创建jav... - xxedgtb
Redis—常见参数配置 - 韭菜园 ... - wdgpjxydo
SpringMVC:Null Model... - rllzzwocp
Mysql存储引擎MyISAM和Inno... - dpkgmbfjh
SpringMVC:Null Model... - tzklbzpj
SpringMVC:Null Model... - bqwrhszmo
MyBatis-Improper inl... - 乐谱吧
good非常好 - diaba
@diaba:应该说是“时间的度量依据”... - diaba
如果速度增加接近光速、等于光速、甚至大于...
最新微语
- 在每件事情上花费的东西,就是生命的一部分,而我们花费的这些东西要求立即得到回报,或者在一个长时间以后得到回报。
2025-01-23 15:46
- 诺曼·文森特说:“并不是你认为自己是什么样的人,你就是什么样的人。但是你的思想是什么样,你就是什么样的人。”
2025-01-23 15:44
- 从今天起,做一个幸福的人。喂马,砍柴,(思想)周游世界
2022-03-21 23:31
- 2022.03.02 23:37:59
2022-03-02 23:38
- 几近崩溃后,找到解决方法,总是那么豁然开朗!所以遇到问题要坚持!
2018-07-18 10:49
发表评论: