I/O多路复用(select/poll/epoll)

news2025/7/17 22:29:25

通过一个进程来维护多个Socket,也就是I/O多路复用,是一种常见的并发编程技术,它允许单个线程或进程同时监视多个输入/输出(I/O)流(例如网络连接、文件描述符)。当任何一个I/O流准备好进行读写操作时,该机制会通知应用程序,从而使得应用程序可以在不为每个连接创建单独线程或进程的情况下,高效地处理多个并发连接。

常见的系统调用有select/poll/epoll,进程可以通过一个系统调用函数从内核中获取多个事件。

select

通过一种“轮询”或者说“检查”的方式工作。应用程序需要告诉内核它关心哪些文件描述符的哪些事件(可读、可写、异常)。其实现多路复用的方式是,将已连接的 Socket 通过FD_SET放入到要监视的文件描述符集中,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。

select需要用户自己维护一个文件描述符数组,需要手动设置关心的事件以及处理完后重设状态,并且这个数组的大小是固定的,因为所要的参数类型是fd_set类型的,这个类型是有固定大小的,即使最终是通过位图来标识文件描述符是否准备就绪的,最大的大小也是sizeof(fd_set) * 8;

其次,select需要遍历文件描述符集合,一次是在内核态里,一个次是在用户态里,而且还会发生2次拷贝文件描述符集合,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。

所以从操作者实用角度和来回拷贝还有文件描述符数量的限制来看,select是有很多缺点的。

poll

poll 不再用位图来存储所关注的文件描述符,取而代之用动态数组,应用程序创建一个 struct pollfd 类型的数组。每个 struct pollfd 结构包含三个主要成员:

  • int fd: 要监视的文件描述符。
  • short events: 一个位掩码,表示应用程序关心该FD上的哪些事件(如 POLLIN 表示可读,POLLOUT 表示可写等)。
  • short revents: 一个位掩码,由内核在 poll 返回时设置,表示该FD上实际发生的事件。

以链表形式来组织,突破了select 的文件描述符个数限制,当然还会受到系统文件描述符限制。
但是 poll 和 select 并没有太大的区别,都需要遍历文件描述符集合来找到可读或可写的Socket,时间复杂度为 0(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长。

epoll

epoll 是对 select 和 poll 的重大改进,特别是在需要监视大量文件描述符时,性能远超前两者。它不再是简单的轮询,而是采用了一种基于事件通知的机制,并且内核会维护一个“就绪列表”。

epoll会用到三个系统调用:

epoll_create:

int epoll_create(int size);创建一个epoll模型,参数 size 在早期版本的内核中用于提示内核期望监视的FD数量,但在现代内核中此参数已被忽略(但仍需大于0)。

返回值是一个文件描述符,后续的操作都将围绕它进行。内核会为这个 epoll 实例维护一个数据结构(通常是红黑树)来存储被监视的FD及其关心的事件,以及一个链表来存放已就绪的FD。

epoll_ctl:

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

第一个参数是epoll_create()的返回值;第二个参数表示动作,用三个宏来表示,第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事。第二个参数的取值:

EPOLL_CTL_ADD :注册新的fd到epfd中;

EPOLL_CTL_MOD :修改已经注册的fd的监听事件;

EPOLL_CTL_DEL :从epfd中删除一个fd;

struct epoll event *event:指向一个结构,该结构定义了要监听epoll event的事件类型(如 EPOLLIN,EPOLLOUT,EPOLLET等)以及可选的用户数据(epoll_data_t)。

在epoll中,不同于select和poll的是:

epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是0(logn)。而 select/poll 内核里没有类似 epoll 红黑树这种保存所有待检测的 socket的数据结构,所以select/poll 每次操作时都传入整个 socket 集合给内核,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。

epoll_wait:

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

events:是一个由应用程序分配的 epoll_event 结构数组,内核会将已就绪的FD及其发生的事件填充到这个数组中。

maxevents:告诉内核 events 数组的大小,即最多可以返回多少个就绪事件。

timeout:超时时间(毫秒)。-1表示无限期阻塞,0表示立即返回。

epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件(就绪队列),当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中(从红黑树链入到就绪队列中),当用户调用 epoll_wait()函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合来找哪一个就绪了,大大提高了检测的效率。

所以总结一下就是:

epoll_create:创建epoll模型

epoll_ctl:在红黑树上增删改

epoll_wait:从就绪队列(这个队列中都是就绪的)中把就绪事件拿出来

epoll支持边缘触发 (Edge Triggered, ET)水平触发 (Level Triggered, LT) 两种模式

LT (默认模式):只要FD处于可读/可写状态,epoll_wait 每次都会返回该FD。如果一次没有完全读/写完数据,下次调用 epoll_wait 仍然会通知该FD就绪。

ET:当FD从未就绪变为就绪状态时,epoll_wait 会通知一次。之后即使FD仍然可读/可写,在没有新的事件(例如新的数据到达)之前,epoll_wait 不会再通知该FD。这要求应用程序在收到通知后必须将该FD上的数据一次性处理完毕(通常是循环读/写直到返回 EAGAINEWOULDBLOCK)。ET模式通常效率更高,但编程更复杂。

在使用ET模式的时候,就需要把对应的文件描述符设置成非阻塞的才可以,因为我们不知道到底能读写多少数据,所以在收到通知后应尽可能地读写数据,以免错失读写的机会。因此,我们会循环从文件描述符读写数据,一次性读完所有的内容,那么如果文件描述符是阻塞读取的,没有数据可读写时,进程会阻塞在读写函数那里,程序就没办法继续往下执行。所以,边缘触发模式一般和非阻塞 I/0 搭配使用,程序会一直执行I/0 操作,直到系统调用(如 read和 write)返回错误,错误类型为EAGAIN或EWOULDBLOCK。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Westlake-Omni 情感端音频生成式输出模型

简述 github地址在 GitHub - xinchen-ai/Westlake-OmniContribute to xinchen-ai/Westlake-Omni development by creating an account on GitHub.https://github.com/xinchen-ai/Westlake-Omni Westlake-Omni 是由西湖心辰(xinchen-ai)开发的一个开源…

随手记录5

一些顶级思维: ​ 顶级思维 1、永远不要自卑。 也永远不要感觉自己比别人差,这个人有没有钱,有多少钱,其实跟你都没有关系。有很多人就是那个奴性太强,看到比自己优秀的人,甚至一些装逼的人,这…

Linux驱动:驱动编译流程了解

要求 1、开发板中的linux的zImage必须是自己编译的 2、内核源码树,其实就是一个经过了配置编译之后的内核源码。 3、nfs挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。 内核源码树 解压 tar -jxvf x210kernel.tar.bz2 编译 make x210ii_qt_defconfigmakeCan’t use ‘…

使用 Flowise 构建基于私有知识库的智能客服 Agent(图文教程)

使用 Flowise 构建基于私有知识库的智能客服 Agent(图文教程) 在构建 AI 客服时,常见的需求是让机器人基于企业自身的知识文档,提供准确可靠的答案。本文将手把手教你如何使用 Flowise + 向量数据库(如 Pinecone),构建一个结合 RAG(Retrieval-Augmented Generation)检…

RabbitMQ ③-Spring使用RabbitMQ

Spring使用RabbitMQ 创建 Spring 项目后&#xff0c;引入依赖&#xff1a; <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --> <dependency><groupId>org.springframework.boot</groupId><artifac…

linux中常用的命令(四)

目录 1-cat查看文件内容 2-more命令 3-less命令 4-head命令 5-tail命令 1-cat查看文件内容 cat中的一些操作 -b : 列出行号&#xff08;不含空白行&#xff09;-E : 将结尾的断行以 $ 的形式展示出来-n : 列出行号&#xff08;含空白行&#xff09;-T : 将 tab 键 以 ^I 显示…

利用SSRF击穿内网!kali靶机实验

目录 1. 靶场拓扑图 2. 判断SSRF的存在 3. SSRF获取本地信息 3.1. SSRF常用协议 3.2. 使用file协议 4. 172.150.23.1/24探测端口 5. 172.150.23.22 - 代码注入 6. 172.150.23.23 SQL注入 7. 172.150.23.24 命令执行 7.1. 实验步骤 8. 172.150.23.27:6379 Redis未授权…

DVWA在线靶场-xss部分

目录 1. xxs&#xff08;dom&#xff09; 1.1 low 1.2 medium 1.3 high 1.4 impossible 2. xss&#xff08;reflected&#xff09; 反射型 2.1 low 2.2 medium 2.3 high 2.4 impossible 3. xss&#xff08;stored&#xff09;存储型 --留言板 3.1 low 3.2 medium 3.3 high 3.…

Go 语言 slice(切片) 的使用

序言 在许多开发语言中&#xff0c;动态数组是必不可少的一个组成部分。在实际的开发中很少会使用到数组&#xff0c;因为对于数组的大小大多数情况下我们是不能事先就确定好的&#xff0c;所以他不够灵活。动态数组通过提供自动扩容的机制&#xff0c;极大地提升了开发效率。这…

js常用的数组遍历方式

以下是一个完整的示例&#xff0c;将包含图片、文字和数字的数组渲染到 HTML 页面&#xff0c;使用 ​多种遍历方式​ 实现不同的渲染效果&#xff1a; 1. 准备数据&#xff08;数组&#xff09; const items [{ id: 1, name: "苹果", price: 5.99, image: "h…

【网络编程】五、三次握手 四次挥手

文章目录 Ⅰ. 三次握手Ⅱ. 建立连接后的通信Ⅲ. 四次挥手 Ⅰ. 三次握手 ​ 1、首先双方都是处于未通信的状态&#xff0c;也就是关闭状态 CLOSE。 ​ 2、因为服务端是为了服务客户端的&#xff0c;所以它会提前调用 listen() 函数进行对客户端请求的监听。 ​ 3、接着客户端就…

从 AGI 到具身智能体:解构 AI 核心概念与演化路径全景20250509

&#x1f916; 从 AGI 到具身智能体&#xff1a;解构 AI 核心概念与演化路径全景 作者&#xff1a;AI 应用实践者 在过去的几年中&#xff0c;AI 领域飞速发展&#xff0c;从简单的文本生成模型演进为今天具备复杂推理、感知能力的“智能体”系统。本文将从核心概念出发&#x…

Docker Compose 的历史和发展

这张图表展示了Docker Compose从V1到V2的演变过程&#xff0c;并解释了不同版本的Compose文件格式及其支持情况。以下是对图表的详细讲解&#xff1a; Compose V1 No longer supported: Compose V1已经不再支持。Compose file format 3.x: 使用了版本3.x的Compose文件格式。 …

从 JIT 即时编译一直讲到CGI|FastGGI|WSGI|ASGI四种协议的实现细节

背景 我一度理解错了这个东西&#xff0c;之前没有AI的时候&#xff0c;也没深究过&#xff0c;还觉得PHP8支持了常驻内存的运行的错误理解&#xff0c;时至今日再来看这个就很清晰了。 另外&#xff0c;早几年对以上4个协议&#xff0c;我也没搞懂&#xff0c;时至今日&…

CSS3 遮罩

在网页设计中&#xff0c;我们经常需要实现一些特殊的视觉效果来增强用户体验。CSS3 遮罩&#xff08;mask&#xff09;允许我们通过控制元素的可见区域来创建各种精美的视觉效果。本文将带你全面了解 CSS3 遮罩的功能和应用。 什么是 CSS3 遮罩&#xff1f; CSS3 遮罩是一种…

ResNet残差神经网络的模型结构定义(pytorch实现)

ResNet残差神经网络的模型结构定义&#xff08;pytorch实现&#xff09; ResNet‑34 ResNet‑34的实现思路。核心在于&#xff1a; 定义残差块&#xff08;BasicBlock&#xff09;用 _make_layer 方法堆叠多个残差块按照 ResNet‑34 的通道和层数配置来搭建网络 import torch…

uniapp|商品列表加入购物车实现抛物线动画效果、上下左右抛入、多端兼容(H5、APP、微信小程序)

以uniapp框架为基础,详细解析商品列表加入购物车抛物线动画的实现方案。通过动态获取商品点击位置与购物车坐标,结合CSS过渡动画模拟抛物线轨迹,实现从商品图到购物车图标的动态效果。 目录 核心实现原理坐标动态计算抛物线轨迹模拟​动画元素控制代码实现详解模板层设计脚本…

谈AI/OT 的融合

过去的十几年间&#xff0c;工业界讨论最多的话题之一就是IT/OT 融合&#xff0c;现在&#xff0c;我们不仅要实现IT/OT 的融合&#xff0c;更要面向AI/OT 的融合。看起来不太靠谱&#xff0c;却留给我们无限的想象空间。OT 领域的专家们不要再当“九斤老太”&#xff0c;指责这…

USB传输模式

USB有四种传输模式: 控制传输, 中断传输, 同步传输, 批量传输 1. 中断传输 中断传输一般用于小批量, 非连续的传输. 对实时性要求较高. 常见的使用此传输模式的设备有: 鼠标, 键盘等. 要注意的是, 这里的 “中断” 和我们常见的中断概念有差异. Linux中的中断是设备主动发起的…

.NET10 - 尝试一下Open Api的一些新特性

1.简单介绍 .NET9中Open Api有了很大的变化&#xff0c;在默认的Asp.NET Core Web Api项目中&#xff0c;已经移除了Swashbuckle.AspNetCore package&#xff0c;同时progrom中也变更为 builder.Servers.AddOpenApi() builder.Services.MapOpenApi() 2025年微软将发布…