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

IO多路复用——Select底层原理深度分析(流程图)

文章目录

  • 1.kern_select 参数验证和初始化流程
  • 2. do_select() 详细实现流程
  • 3. 位图数据结构详解
  • 4. 文件描述符处理详细流程
  • 5. Poll方法调用链
  • 6. 等待机制实现
  • 7. 用户态处理就绪事件
  • 8. 性能瓶颈分析
  • 9. 与其他I/O多路复用对比

Select 是 Linux 系统中最早的 I/O 多路复用机制,它允许程序同时监控多个文件描述符的状态变化。

Select 使用位图(fd_set)来表示要监控的文件描述符集合,通过系统调用进入内核后,内核会遍历位图中设置为 1 的每一位,对每个对应的文件描述符调用其 poll 方法来检查是否有数据可读、可写或发生异常。

当有事件就绪时,内核会将结果位图拷贝回用户空间,用户程序通过检查结果位图来确定哪些文件描述符发生了事件。

Select 的主要特点是实现简单、兼容性好,但也存在明显的性能瓶颈:每次调用都需要遍历所有文件描述符(时间复杂度 O(n)),最多只能监控 1024 个文件描述符,并且每次调用都要进行两次用户态和内核态之间的数据拷贝。

因此,Select 适用于连接数较少的场景,在高并发环境下性能较差,这也是后来出现 Poll 和 Epoll 等更高效 I/O 多路复用机制的原因。

  • Select 整体调用流程:
用户空间调用select方法
系统调用入口 SYSCALL_DEFINE5
kern_select 参数验证和初始化
do_select 核心实现
遍历文件描述符位图
对每个fd调用poll方法
检查事件状态
更新结果位图
有事件就绪?
返回结果到用户空间
用户空间轮询处理相应事件
等待事件或超时
被唤醒后重新检查

1.kern_select 参数验证和初始化流程

kern_select开始
声明数据结构
参数验证
n < 0 或 n > FD_SETSIZE?
返回 -EINVAL
初始化poll_wqueues
设置select_table
调用do_select
清理资源
返回结果
结束
  • poll_wqueues: Linux 内核中用于管理 poll/select 等待队列的核心数据结构。
  • select_table: Select 系统调用中连接用户空间和内核空间 poll 机制的桥梁数据结构。

2. do_select() 详细实现流程

全为0
有设置位
do_select开始
拷贝fd_set到内核空间
初始化变量和指针
遍历每个unsigned long 64位组
检查当前64位组
跳过整个64位组
下一个64位组
遍历当前64位组的每一位
当前位是否设置?
下一位
获取文件描述符fdget_i
文件描述符有效?
释放文件描述符
调用文件的poll方法
检查事件状态
设置结果位
遍历完当前64位组?
更新结果位图到用户空间
有事件就绪?
返回事件数量
调用poll_schedule_timeout等待
被唤醒或超时
重新检查事件状态
结束
  • fd_set: 表示文件描述符集合的位图数据结构
    • 固定大小:__FD_SETSIZE = 1024
    • 在64位系统上:1024 / 64 = 16 个 unsigned long、
    • 总共可以表示 1024 个文件描述符

3. 位图数据结构详解

位值含义
位图含义
fd_set 位图结构
1: 监控
0: 不监控
位1: 文件描述符1
位0: 文件描述符0
位2: 文件描述符2
...
位1023: 文件描述符1023
bits_1 64位
bits_0 64位
bits_2 64位
...
bits_15 64位

4. 文件描述符处理详细流程

用户空间 内核空间 文件系统 Socket层 select() 系统调用 从用户空间拷贝fd_set到内核 遍历位图 fdget(i) 获取文件描述符 返回文件对象 调用sock_poll() 检查socket状态 返回事件掩码 根据事件掩码设置结果位 fdput(f) 释放文件描述符 loop [对每个设置的文件描述符] 更新结果位图 返回结果到用户空间 用户空间 内核空间 文件系统 Socket层

5. Poll方法调用链

TCP
UDP
其他
do_select调用poll
sock_poll
检查socket类型
tcp_poll
udp_poll
默认poll
检查TCP连接状态
检查接收缓冲区
检查发送缓冲区
检查错误状态
检查UDP数据
返回默认事件
返回事件掩码
设置结果位

6. 等待机制实现

进程运行中
没有事件就绪
poll_schedule_timeout
文件描述符状态改变
超时时间到达
收到信号
重新检查事件
重新检查事件
重新检查事件
返回结果
Running
Interruptible
Waiting
Woken
Timeout
Signal

7. 用户态处理就绪事件

用户程序收到返回值后,会处理相应事件:

int ready = select(n, &readfds, &writefds, &exceptfds, &timeout);
if (ready > 0) {// 检查哪些文件描述符就绪for (int i = 0; i < n; i++) {if (FD_ISSET(i, &readfds)) {// 文件描述符 i 有数据可读read_data(i);}if (FD_ISSET(i, &writefds)) {// 文件描述符 i 可以写入数据write_data(i);}if (FD_ISSET(i, &exceptfds)) {// 文件描述符 i 发生异常handle_exception(i);}}
}

8. 性能瓶颈分析

Select性能瓶颈
遍历开销
内存拷贝
文件描述符限制
重复检查
O_n 时间复杂度
每次都要遍历所有fd
即使大部分fd没有事件
每次调用都拷贝fd_set
结果也要拷贝回用户空间
用户态内核态切换开销
最多1024个文件描述符
无法动态扩展
不适合高并发场景
每次重新检查所有fd状态
没有事件驱动机制
轮询方式效率低

9. 与其他I/O多路复用对比

Epoll
Poll
Select
时间复杂度: O_1
fd限制: 无限制
内存拷贝: 只在添加时
事件通知: 事件驱动
数据结构: 红黑树+链表
时间复杂度: O_n
fd限制: 无限制
内存拷贝: 每次调用
事件通知: 轮询
数据结构: 数组
时间复杂度: O_n
fd限制: 1024
内存拷贝: 每次调用
事件通知: 轮询
数据结构: 位图
http://www.lqws.cn/news/508105.html

相关文章:

  • 【C/C++】趣味题目:二维数组地址
  • 【开源项目】比 PyInstaller 更方便:图形界面打包 Python 脚本的体验
  • MySQL MVCC(多版本并发控制)详解
  • 华为云Flexus+DeepSeek征文|DeepSeek-V3与R1的差异化体验
  • iwebsec靶场-文件上传漏洞
  • Vue 3 和 Ant Design 版本3.2.20 Table解析
  • OSS阿里云
  • 实现OFD转换PDF文件的实用方法
  • Joblib库多进程/线程使用(一):使用generator参数实现边响应边使用
  • RISC-V三级流水线项目:总体概述和取指模块
  • 借助antd-design-x-vue实现接入通义千问大语言模型的对话功能(二) 新增思考内容展示
  • 使用 PyFluent 自动化 CFD
  • Reactor框架介绍,和使用示例
  • 内存泄漏和内存溢出的区别
  • ubuntu22.04修改IP地址
  • Camera Api 2 和 OPEN GL ES 使用(显示滤镜效果)
  • 【网络安全】从IP头部看网络通信:IPv4、IPv6与抓包工具 Wireshark 实战
  • 基于单片机的语音控制设计(论文)
  • 618风控战升级,瑞数信息“动态安全+AI”利剑出鞘
  • 09-StarRocks安全配置FAQ
  • 03-StarRocks查询优化FAQ
  • 服务器安装指南
  • PYTHON从入门到实践2-环境配置与字符串打印用法
  • 黑马Day01-03集开始
  • 轻量化实物建模革命:WebGL如何实现复杂模型的高效加载与交互
  • 增刊第2章:模型API封装与安全
  • 从虚拟机角度解释python3相对导入问题(下)
  • CasaOS中Docker部署SyncThing结合Cpolar实现公网文件同步方案
  • MemcacheRedis--缓存服务器理论
  • 解锁阿里云AnalyticDB:数据仓库的革新利器