当前位置: 首页 > news >正文

IO多路复用——Poll底层原理深度分析

文章目录

  • 1. 工作流程
  • 2. 详细文字描述
    • 2.1 初始化阶段
    • 2.2 监听阶段
    • 2.3 事件处理阶段
    • 2.4 循环监听
  • 3.Poll与Select底层原理对比
    • 3.1 系统调用层面
      • Select系统调用
      • Poll系统调用
    • 3.2 核心数据结构差异
      • Select的数据结构
      • Poll的数据结构
    • 3.3 底层实现机制对比
    • 3.4 详细底层原理分析
      • 文件描述符传递方式
      • 内核处理流程
    • 3.5 性能差异分析
      • 时间复杂度
      • 内存使用
    • 3.6 系统调用开销对比
    • 3.7 实际性能测试场景
      • 场景1:监听少量文件描述符(< 100)
      • 场景2:监听大量文件描述符(> 1000)
      • 场景3:高并发场景
    • 3.8 底层实现细节对比

Poll是IO多路复用的一种实现方式,它允许一个进程同时监听多个文件描述符,当其中任何一个文件描述符就绪时,进程就可以进行相应的IO操作。

  • 核心特点:
    • 非阻塞:不会因为某个文件描述符未就绪而阻塞整个进程
    • 轮询机制:通过轮询检查所有被监听的文件描述符状态
    • 事件驱动:基于事件通知机制,只有文件描述符状态发生变化时才返回

1. 工作流程

POLLIN
POLLOUT
POLLERR
应用程序启动
创建pollfd数组
设置要监听的文件描述符和事件
调用poll函数
检查所有文件描述符
是否有文件描述符就绪?
超时或继续等待
返回就绪的文件描述符数量
遍历pollfd数组
检查revents字段
处理读事件
处理写事件
处理错误事件
执行相应的IO操作
是否继续监听?
结束程序

2. 详细文字描述

2.1 初始化阶段

  1. 创建pollfd结构体数组:为每个需要监听的文件描述符分配一个pollfd结构体
  2. 设置监听参数
    • fd:要监听的文件描述符
    • events:要监听的事件类型(读、写、异常等)
    • revents:实际发生的事件(由内核填充)

2.2 监听阶段

  1. 调用poll函数:将pollfd数组传递给内核
  2. 内核处理
    • 检查所有文件描述符的状态
    • 将就绪的文件描述符标记在revents字段中
    • 返回就绪的文件描述符数量

2.3 事件处理阶段

  1. 遍历pollfd数组:检查每个文件描述符的revents字段
  2. 事件分类处理
    • POLLIN:数据可读
    • POLLOUT:数据可写
    • POLLERR:发生错误
    • POLLHUP:连接断开

2.4 循环监听

处理完当前事件后,重新调用poll函数继续监听,形成事件循环。

3.Poll与Select底层原理对比

3.1 系统调用层面

Select系统调用

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

Poll系统调用

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

3.2 核心数据结构差异

Select的数据结构

typedef struct {long fds_bits[FD_SETSIZE / NFDBITS];
} fd_set;// 位图表示,每个bit代表一个文件描述符
// 例如:fds_bits[0] = 0x00000001 表示文件描述符0被设置

Poll的数据结构

struct pollfd {int   fd;      // 文件描述符short events;  // 请求的事件short revents; // 返回的事件
};

3.3 底层实现机制对比

都有两次用户空间、内核空间之间的拷贝。

Poll机制
内核空间
用户空间
遍历pollfd数组
检查每个fd的状态
修改revents字段
返回就绪数量
用户遍历pollfd数组
Select机制
内核空间
用户空间
遍历所有文件描述符
检查每个fd的状态
修改位图
返回就绪数量
用户再次遍历位图

3.4 详细底层原理分析

文件描述符传递方式

Select:

  • 使用三个独立的位图(readfds, writefds, exceptfds)
  • 每个位图使用long数组存储,每个bit代表一个文件描述符
  • 文件描述符范围限制:0 到 FD_SETSIZE-1(通常是1024)

Poll:

  • 使用pollfd结构体数组
  • 每个结构体包含文件描述符和事件信息
  • 无文件描述符数量限制

内核处理流程

Select内核处理:

// 伪代码展示select内核处理
int do_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) {// 1. 遍历所有文件描述符(0到nfds-1)for (int fd = 0; fd < nfds; fd++) {// 2. 检查每个fd是否在对应的位图中if (FD_ISSET(fd, readfds)) {// 3. 检查该fd是否可读if (is_readable(fd)) {// 4. 设置对应的位FD_SET(fd, readfds);} else {// 5. 清除对应的位FD_CLR(fd, readfds);}}// 类似处理writefds和exceptfds}return ready_count;
}

Poll内核处理:

// 伪代码展示poll内核处理
int do_poll(struct pollfd *fds, nfds_t nfds) {// 1. 遍历pollfd数组for (int i = 0; i < nfds; i++) {int fd = fds[i].fd;short events = fds[i].events;// 2. 检查该fd的状态short revents = 0;if (events & POLLIN && is_readable(fd)) {revents |= POLLIN;}if (events & POLLOUT && is_writable(fd)) {revents |= POLLOUT;}// 3. 设置revents字段fds[i].revents = revents;}return ready_count;
}

3.5 性能差异分析

时间复杂度

Select:

  • 遍历复杂度:O(n),其中n是最大文件描述符值
  • 即使只监听文件描述符1000,也要检查0-999的所有fd

Poll:

  • 遍历复杂度:O(n),其中n是实际监听的文件描述符数量
  • 只检查用户实际设置的pollfd结构体

内存使用

Select:

// 内存布局示例
fd_set readfds;  // 128字节(1024位)
fd_set writefds; // 128字节
fd_set exceptfds; // 128字节
// 总计:384字节固定大小

Poll:

// 内存布局示例
struct pollfd fds[100];  // 每个结构体12字节
// 总计:1200字节(可动态调整)

3.6 系统调用开销对比

Poll开销
Select开销
系统调用
用户空间
内核遍历实际fd数量
修改revents字段
返回用户空间
用户遍历pollfd数组
系统调用
用户空间
内核遍历1024个fd
修改位图
返回用户空间
用户遍历位图

3.7 实际性能测试场景

场景1:监听少量文件描述符(< 100)

  • Select性能:较好,因为位图操作效率高
  • Poll性能:相当,结构体数组开销不大

场景2:监听大量文件描述符(> 1000)

  • Select性能:较差,需要遍历大量无效fd
  • Poll性能:较好,只处理实际监听的fd

场景3:高并发场景

  • Select性能:每次都要重新设置位图
  • Poll性能:可以复用pollfd数组

3.8 底层实现细节对比

特性SelectPoll
数据结构位图结构体数组
文件描述符限制1024无限制
内核遍历方式线性扫描位图线性扫描数组
用户空间处理位操作数组遍历
内存效率固定大小动态大小
系统调用参数多个独立参数单个结构体数组
http://www.lqws.cn/news/514423.html

相关文章:

  • 深入解析RS485通信:从原理到Linux驱动开发实践
  • DeepSeek在数据分析与科学计算中的革命性应用
  • “易问易视”——让数据分析像聊天一样简单
  • 终止分区表变更操作时误删数据字典缓存导致MySQL崩溃分析
  • 【网站内容安全检测】之2:从网站所有URL页面中提取所有外部及内部域名信息
  • 批量DWG转PDF工具
  • 提供一种在树莓派5上切换模式的思路(本文是面向显示屏配置文件)
  • LVS-DR负载均衡群集深度实践:高性能架构设计与排障指南
  • BUUCTF在线评测-练习场-WebCTF习题[ACTF2020 新生赛]BackupFile1-flag获取、解析
  • 一款实验室创客实验室用的桌面式五轴加工中心
  • 04-html元素列表-表格-表单
  • django request.data.get 判断有没有 某个参数
  • GROUP BY、UNION和COALESCE协作
  • 电商导购app平台的缓存策略与性能优化方案:架构师的实践经验
  • 【番外篇】TLS指纹
  • 4.1 ROS颜色目标识别与定位
  • 【大厂机试题解法笔记】分解连续正整数组合/ 分解正整数
  • 探索解析C++ STL中的 list:双向链表的高效实现与迭代器
  • 领域驱动设计(DDD)【13】之重构中的坏味道:深入理解依恋特性(Feature Envy)与表意接口模式
  • 没有VISA怎么注册AWS?
  • Flutter - 原生交互 - 相册
  • C语言基础回顾与Objective-C核心类型详解
  • QT 学习笔记摘要(三)
  • 每日AI资讯速递 | 2025-06-25
  • TDengine 的 CASE WHEN 语法技术详细
  • 磐维数据库PanWeiDB V2.0-S3.1.1_B01集中式一主二备安装
  • linux安装docker
  • Android14音频子系统-ASoC-ALSA之DAPM电源管理子系统
  • ISO/IEC 27001:2022 資訊安全管理系統 Information Security Management System , ISMS
  • elementui修改radio字体的颜色和圆圈的样式