插件窝 干货文章 linux中poll和select有什么区别

linux中poll和select有什么区别

描述 select poll 调用 877    来源:    2025-05-03

在 Linux 中,pollselect 都是用于 I/O 多路复用的系统调用,允许程序同时监控多个文件描述符(如套接字、管道等)的读写状态。尽管它们功能相似,但在实现、性能和扩展性上有显著区别。以下是两者的详细对比:


1. 基本区别

特性 select poll
系统调用原型 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int poll(struct pollfd *fds, nfds_t nfds, int timeout);
文件描述符集合表示 使用 fd_set 位掩码(固定大小) 使用 pollfd 结构体数组(动态)
最大文件描述符限制 FD_SETSIZE 限制(通常 1024) 无硬编码限制(取决于系统资源)
性能 每次调用需重新设置 fd_set 只需修改变化的 pollfd 条目
可读性 接口复杂,需手动管理位掩码 接口更清晰,直接操作结构体

2. 关键差异详解

(1) 文件描述符管理

  • select

    • 使用三个独立的 fd_set 位掩码(readfds, writefds, exceptfds)监控描述符。
    • 每次调用后,内核会修改 fd_set,因此下次调用需重新初始化。
    • 最大描述符数由 FD_SETSIZE 定义(默认 1024),修改需重新编译内核。
  • poll

    • 使用 pollfd 结构体数组,每个元素包含 fdevents(监控的事件)和 revents(实际发生的事件)。
    • 无内置描述符数量限制,动态分配内存即可。
    • 通过 revents 字段返回事件,无需每次重置整个数组。

(2) 性能

  • select

    • 每次调用需将整个 fd_set 从用户态拷贝到内核态,描述符多时开销较大。
    • 内核需线性扫描所有描述符(即使未就绪),时间复杂度为 O(n)。
  • poll

    • 只需传递 pollfd 数组指针,拷贝开销相对较小。
    • 同样需线性扫描,但无 FD_SETSIZE 限制,更适合高并发场景。

(3) 事件类型

  • select

    • 仅支持三类事件:可读(readfds)、可写(writefds)和异常(exceptfds)。
  • poll

    • 支持更丰富的事件(如 POLLINPOLLPRIPOLLOUTPOLLRDHUP 等),可通过 events 字段灵活组合。

(4) 可移植性

  • select 是 POSIX 标准,几乎所有系统支持。
  • poll 在大多数现代 Unix 系统上可用,但某些旧系统(如早期 BSD)可能不支持。

3. 示例代码对比

select 示例

fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);

struct timeval timeout = {5, 0}; // 5秒超时
int ret = select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
if (ret > 0 && FD_ISSET(sockfd, &read_fds)) {
    // sockfd 可读
}

poll 示例

struct pollfd fds[1];
fds[0].fd = sockfd;
fds[0].events = POLLIN;

int ret = poll(fds, 1, 5000); // 5秒超时
if (ret > 0 && (fds[0].revents & POLLIN)) {
    // sockfd 可读
}

4. 如何选择?

  • 优先用 poll

    • 需要监控的描述符超过 1024。
    • 需要更精细的事件控制(如 POLLRDHUP 检测对端关闭)。
    • 避免频繁重置描述符集合。
  • 使用 select

    • 需要兼容旧系统(如某些嵌入式环境)。
    • 描述符数量少且对性能不敏感。
  • 更高性能场景

    • 考虑使用 epoll(Linux 特有,适用于大量描述符且活跃比例低的场景)。

5. 总结

  • select:简单但扩展性差,适合低并发或兼容性要求高的场景。
  • poll:更灵活且无描述符限制,推荐大多数现代应用。
  • epoll/kqueue:Linux/BSD 的高性能替代方案,需针对平台优化。

通过理解两者的差异,可以根据实际需求选择最合适的 I/O 多路复用机制。