poll

 poll vs select 核心区别

select就像老式电话总机
  • 接线员要记住所有电话号码
  • 每次都要问:"1号?2号?3号?..."
  • 最多只能记1024个号码
poll就像现代呼叫系统
  • 系统自动记录所有房间号
  • 哪个房间按铃,灯就亮
  • 没有数量限制

 poll的基本结构

poll的核心数据结构

#include <poll.h>
struct pollfd {
    int fd;        // 文件描述符
    short events;  // 要监听的事件
    short revents; // 实际发生的事件
};

poll函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数说明

fd
  • 要监听的文件描述符。
  • 如果 fd = -1,则 poll 会忽略这个结构体(不监听)。
events(要监听的事件,位掩码):
  • POLLIN:有数据可读(包括普通数据、优先级数据)
  • POLLPRI:有紧急数据可读(比如 TCP 带外数据)
  • POLLOUT:可以写数据(内核发送缓冲区未满)
  • POLLERR:错误(只作为输出事件,不需要设置)
  • POLLHUP:挂起(比如管道写端关闭)
  • POLLNVAL:fd 无效
revents(返回的事件):
  • poll 返回时,内核会在这里标记实际发生的事件。
  • 可能包含 POLLINPOLLOUTPOLLERR 等。

(2) nfds —— 数组元素个数

  • 类型是 nfds_t(实际上就是 unsigned int
  • 告诉 poll 数组 fds 里有多少个元素需要监听

(3) timeout —— 超时时间(毫秒)

  • timeout > 0:最多等待 timeout 毫秒后返回
  • timeout == 0:立即返回,不阻塞(轮询)
  • timeout == -1:一直阻塞,直到有事件发生或被信号中断

返回值

  • > 0:有事件发生的文件描述符数量
  • == 0:超时,没有事件发生
  • == -1:出错,errno 会说明原因(如被信号中断 EINTR

使用步骤

  1. 定义 struct pollfd 数组,设置每个 fd 要监听的事件
  2. 调用 poll 等待事件
  3. 遍历数组,检查 revents 看哪些 fd 有事件

3. 从select迁移到poll

3.1 先看select的写法(复习)

// select的方式
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
FD_SET(client1, &readfds);

select(maxfd+1, &readfds, NULL, NULL, NULL);

if (FD_ISSET(sockfd, &readfds)) {
    // 处理新连接
}
if (FD_ISSET(client1, &readfds)) {
    // 处理客户端数据
}

3.2 同样的功能用poll实现

// poll的方式
struct pollfd fds[2];

fds[0].fd = sockfd;      // 服务器socket
fds[0].events = POLLIN;  // 监听读事件

fds[1].fd = client1;     // 客户端socket  
fds[1].events = POLLIN;  // 监听读事件

int n = poll(fds, 2, -1);  // 监听2个fd,无限等待

if (fds[0].revents & POLLIN) {
    // 处理新连接
}
if (fds[1].revents & POLLIN) {
    // 处理客户端数据
}

poll的事件类型

// 常用的事件类型
POLLIN    // 数据可读
POLLOUT   // 数据可写  
POLLERR   // 发生错误
POLLHUP   // 连接挂断
POLLNVAL  // 无效的fd

// 组合使用
fds[i].events = POLLIN | POLLOUT;  // 同时监听读和写

// 检查多个事件
if (fds[i].revents & (POLLIN | POLLERR)) {
    // 有数据可读或发生错误
}

// 在头文件 poll.h 中定义:

#define POLLIN   0x001   // 二进制: 0000 0000 0001
#define POLLOUT  0x004   // 二进制: 0000 0000 0100  
#define POLLERR  0x008   // 二进制: 0000 0000 1000
#define POLLHUP  0x010   // 二进制: 0000 0001 0000


// 可以同时监听多个事件
fds[i].events = POLLIN | POLLOUT;  
// 相当于: 0x001 | 0x004 = 0x005
// 二进制: 0000 0000 0101 (同时设置了第1位和第3位)

// 当事件发生时,内核会设置相应的位
// 比如同时有数据可读和可写:
fds[i].revents = POLLIN | POLLOUT;  // 0x005


// 例子1:检查是否有POLLIN事件
fds[i].revents = 0x005;  // 二进制: 0000 0000 0101 (有POLLIN和POLLOUT)
POLLIN = 0x001;          // 二进制: 0000 0000 0001

0x005 & 0x001 = 0x001    // 结果不为0 → 有POLLIN事件

// 例子2:检查是否有POLLERR事件  
fds[i].revents = 0x005;  // 二进制: 0000 0000 0101
POLLERR = 0x008;         // 二进制: 0000 0000 1000

0x005 & 0x008 = 0x000    // 结果为0 → 没有POLLERR事件

特性
select
poll
最大fd数
1024
无限制
效率
O(n)扫描
O(n)扫描
内存使用
固定位图
动态数组
平台支持
所有Unix
大部分Unix
使用难度
简单
简单

poll的优点

  1. 没有fd数量限制(select只有1024)
  2. 不需要计算maxfd+1
  3. 事件类型更丰富
  4. API更直观
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐