【Linux】select,poll和epoll
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符fd,一旦某个描述符就绪(一般是读就绪或者写就绪),系统会通知有I/O事件发生了(不能定位是哪一个)。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
select、poll和epoll这三个函数是Linux系统中I/O复用的系统调用函数。I/O复用使得这三个函数可以同时监听多个文件描述符(File Descriptor, FD),因为每个文件描述符相当于一个需要 I/O的“文件”,在socket中共用一个端口。
select
作用:监控多个文件描述符,就绪之后通知调用者
工作原理

函数
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *execptfds, struct timeval *timeout);
参数:
nfds:参数指定被监听的文件描述符的总数。他通常被设置为select监听所有文件描述符中最大值加1,因为文件描述符是从0开始计数的。
readfds、writefds和exceptfds参数分别指向可读、可写和异常等事件对应的文件描述符集合
timeout参数是用来设置select函数的超时时间。
返回值:
select成功时返回就绪(可读、可写和异常)文件描述符的总数。
select失败时将返回-1并设置errno。
timeout参数是用来设置select函数的超时时间。它是一个timeval结构类型的指针,其定义如下:
struct timeval{long tv_sec;/*秒数*/long tv_usec;/*微秒数*/
};
注:当我们给timeout设定了特定的时间后,假设是10s,如果select在10s内调用返回(3s就返回了),那么timeval的成员tv_sec和tv_usec的值将会被替换成剩余的秒数。
内核在使用该数组fd_set的时候采用的是位图的方式,一共有16*8*8=1024个比特位。其实fd_set结构就是一个整数数组,更严格的是,是一个“位图”,使用位图中对应的位来表示要监控的文件描述符,如下图所示:
为了方便操的作位图,可使用如下接口来操作fd_set:
从事件集合当中删除一个文件描述符:
void FD_CLR(int fd, fd_set *set);
判断文件描述符是否在某一个事件集合当中:
int FD_ISSET(int fd, fd_set *set);
设置文件描述符到事件集合当中:
void FD_SET(int fd, fd_set *set);
清空事件集合,将所有的比特位全部置为0:
void FD_ZERO(fd_set *set);
select的优点和缺点
优点:
- select遵循的是POSIX标准,说明select函数是一个跨平台的函数,既可以在Windows当中运行,也可以在Linux当中运行
- select在带有超时时间的监控的时候,超时时间的单位可以为微妙
缺点:
- 监控文件描述符个数的上限为1024
- 随着文件描述符的增多,select监控效率在下降(本质是select在轮询进行监控)
- 可读、可写、异常这些事件需要单独的添加到不同的事件集合当中
- 当select监控成功之后,会从事件集合当中去除掉未就绪的文件描述符,这使程序下一次调用select时,还需要重新添加文件描述符
- 在每次select进行监控的时候,都会将准备好的事件集合拷贝到内核空间,select返回的时候都会将内核空间拷贝给用户空间
1 #include<stdio.h> 2 #include<unistd.h>3 #include<sys/select.h>4 5 int main() 6 { 7 fd_set readfds; 8 FD_ZERO(&readfds);9 FD_SET(0,&readfds);10 11 while(1) 12 { 13 int ret = select(1,&readfds,NULL,NULL,NULL);14 if(ret < 0) 15 {16 perror("select");17 return 0;18 }19 20 char buf[1024] = {0};21 read(0,buf,sizeof(buf)-1);22 23 printf("%s\n",buf);24 } 25 26 return 0; 27 }
poll
可以认为poll是一个增强版本的select,因为select的比特位操作决定了一次性最多处理的读或者写事件(文件描述符数量)只有1024个,而poll使用一个新的方式优化了这个模型。
poll底层操作的数据结构pollfd:
struct pollfd {int fd; // 需要监视的文件描述符short events; // 需要内核监视的事件short revents; // 实际发生的事件
};
函数
int poll(struct pollfd *fds,nfds_t nfds,int timeout);
参数:
fds:想让poll监控多个文件描述符,只需要在定义事件结构数组的时候,多传递几个元素
eg:struct pollfd arr[10];
arr[0].fd = 0;
arr[0].events = POLLIN;
nfds:事件结构数组中有效元素的个数
timeout:
>0:带有超时时间,单位:秒
==0:非阻塞
<0:阻塞
返回值:就绪文件描述符的个数
内核修改的是revents的值,events是我们需要关注的文件描述符,revents是内核检测到文件描述符中有数据的而进行修改后的内容,
poll的优点和缺点
优点:
- 提出了事件结构的方式,在给poll函数传递参数的时候,不需要分别添加到“事件集合”中
- 事件结构数组的大小可以根据程序员自己进行定义,并没有上限要求
- 不用在监控到就绪文件描述符之后,重新添加文件描述符
缺点:
- 不支持跨平台
- 内核对事件结构数组的监控也是采用轮询遍历的方式,即随着监控文件描述符的增多,监控效率会下降
- 每次调用poll都需要把大量的pollf结构从用户态拷贝到内核态,poll返回的时候,会将内核空间拷贝给用户空间(从内核态到用户态会调用do_signal会有开销)
1 #include<stdio.h>2 #include<poll.h>3 #include<unistd.h>4 5 int main()6 {7 struct pollfd arr[10]; 8 9 arr[0].fd = 0;10 arr[0].events = POLLIN;11 12 while(1)13 {14 int ret = poll(arr,1,-1);15 if(ret < 0)16 {17 perror("poll");18 return 0;19 }20 for(int i = 0;i < 10;++i)21 {22 if(arr[i].revents == POLLIN)23 { 24 char buf[1024] = {0};25 read(arr[i].fd,buf,sizeof(buf)-1);26 printf("%s\n",buf);27 } 28 } 29 } 30 return 0; 31 }
epoll
epoll给出了一个新的模式,直接申请一个epollfd的文件,对这些进行统一的管理,初步具有了面向对象的思维模式。可理解为event poll,epoll会把哪个流发生哪种I/O事件通知我们。所以epoll是事件驱动(每个事件关联fd)的,此时我们对这些流的操作都是有意义的。复杂度也降低到了O(1)。
epoll通过在Linux内核中申请一个简易的文件系统。把原先的select/poll调用分成了3个部分:
- 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源);
- 调用epoll_ctl向epoll对象中添加这些连接的套接字;
- 调用epoll_wait收集发生的事件的连接.
如此一来只需要在进程启动时建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有一股脑的向操作系统复制这些连接的句柄数据,内核也不需要去遍历全部的连接。
工作原理

当调用epoll_create函数时,会在内核创建一个eventpoll结构体,在该结构体中有一个rdlist成员和rbr成员,它两分别是一个双向链表和红黑树,而调用epoll_ctl函数添加、修改、删除文件描述符对应的事件集合其实是对红黑树中的节点进行相应的添加、修改、删除操作,而所有添加到epoll的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当文件描述符准备就绪后,内核会回调ep_poll_callback函数,将准备就绪的事件集合添加到rdlist双向链表中,而当调用epoll_wait进行监控的时候,如果双向链表为空,则表明当前没有就绪的事件发生,如果不为空,则将双向链表中的内容复制到用户态,并返回将事件数量返回给用户。
函数
epoll_create
//创建一个epoll模型
int epoll_create(int size);
参数:
size参数自从Linux2.6.8之后,size参数是被忽略的,但size的值必须设置为大于0的值。size只是给内核一个提示,告诉它事件需要多大。仅仅只是给内核一个建议,具体多大还是操作系统说了算。
返回值:
epoll模型创建成功返回其对应的文件描述符,否则返回-1,同时错误码会被设置。
该函数返回的文件描述符将作为其他epoll系统调用的第一个参数,以指定要访问的内核事件表。
当不再使用时,必须调用close函数关闭epoll模型对应的文件描述符,当所有引用epoll实例的文件描述符都已关闭时,内核将销毁该实例并释放相关资源。
epoll_ctl
//向指定的epoll模型中注册事件
int epoll_ctl(int epfd,int op, int fd, struct epoll_event *event);
参数:
epfd:epoll操作句柄
op:告诉epoll要做什么是事
① EPOLL_CTL_ADD:添加一个文件描述符对应的事件结构到epoll当中
② EPOLL_CTL_MOD:修改一个文件描述符的事件结构
③ EPOLL_CTL_DEL:从epoll当中删除一个文件描述符对应的事件结构
fd:待处理(添加、修改、删除)的文件描述符
event:文件描述符对应的事件结构
epoll_event结构体
返回值:
epoll_ctl成功时返回0,失败则返回-1并设置errno。
epoll_wait
//用于收益监视的事件中已经就绪的事件
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout)
参数:
epfd:epoll的操作句柄
events:时间结构数组(集合),从epoll当中获取就绪的事件结构
maxevents:最多一次获取多少个事件结构
timeout:
0:带有超时事件
==0:非阻塞
<0:阻塞返回值:就绪的文件描述符个数
epoll的优点和缺点
优点:
- 事件回调机制,当文件描述符就绪之后,会调用回调函数将事件结构复制到双向链表中,epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度O(1). 即使文件描述符数目很多, 效率也不会受到影响.
- 数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)
- 没有数量限制: 文件描述符数目无上限.
LT和ET模式
首先,select、poll和epoll的工作模式默认情况下都是LT模式。但是epoll的工作模式有两种,分别为LT模式(水平触发模式)和 ET 模式(边缘触发模式)。
LT模式:举一个例子,张三买了很多快递,快递站的小王每隔一段时间通知张三取快递(张三没有去取快递或没取完的情况下,就会一直通知,这不就是轮询嘛)
ET模型:举一个例子,张三买了很多快递,快递站的小李会通知张三取快递,但是只通知一次(张三没有去取快递或没取完的情况下,小李在也不会通知了)
epoll默认是LT模式,我们要将其改为ET模式,只需要将其时间或上一个EPOLLET,在刚才的代码中有体现,可以尝试运行此服务器,测试其效果(我们测试时,只要关心监听套接字就足够了,其将accept函数部分注释掉。然后LT模式下,就会发现,当套接字有读事件就绪后,会不断的触发提示,告诉你有事件就绪;ET模式只会通知一次)
总结

相关文章:
【Linux】select,poll和epoll
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符fd,一旦某个描述符就绪(一般是读就绪或者写就绪),系统会通知有I/O事件发生了(不能定位是哪一个)。但sel…...
Qt程序发布及打包成exe安装包
参考:Qt之程序发布以及打包成exe安装包 目录 一、简述 Qt 项目开发完成之后,需要打包发布程序,而因为用户电脑上没有 Qt 配置环境,所以需要将 release 生成的 exe 文件和所依赖的 dll 文件复制到一个文件夹中,然后再用 Inno Setup 打包工具打包成一个 exe 安装包,就可以…...
python怎样运行js语句
1. 安装 pip install PyExecJS # 需要注意, 包的名称:PyExecJS 2. 简单使用 import execjs execjs.eval("new Date") 返回值为: 2018-04-04T12:53:17.759Z execjs.eval("Date.now()") 返回值为:152284700108…...
汽车渲染领域:Blender 和 UE5 哪款更适用?两者区别?
在汽车渲染领域,选择合适的工具对于实现高质量的视觉效果至关重要。Blender和UE5(Unreal Engine 5)作为两大主流3D软件,各自在渲染动画方面有着显著的差异。本文将从核心定位与用途、工作流程、渲染技术和灵活性、后期处理与合成四…...
JAVA实现将PDF转换成word文档
POM.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.…...
前端-Git
一.基本概念 Git版本控制系统时一个分布式系统,是用来保存工程源代码历史状态的命令行工具 简单来说Git的作用就是版本管理工具。 Git的应用场景:多人开发管理代码;异地开发,版本管理,版本回滚。 Git 的三个区域&a…...
如何分析Windows防火墙日志
Windows防火墙,也被称为Windows Defender Firewall,是一种内置的安全功能,可以主动监控和分析运行Windows操作系统的计算机上通过Windows防火墙的网络流量,主要目的是作为计算机和互联网或其他网络之间的屏障,使管理员…...
工作坊报名|使用 TEN 与 Azure,探索你的多模态交互新场景
GPT-4o Realtime API 发布,语音 AI 技术正在进入一场新的爆发。语音AI技术的实时语音和视觉互动能力将为我们带来更多全新创意和应用场景。 实时音频交互: 允许应用程序实时接收并响应语音和文本输入。自然语音生成: 减少 AI 技术生成的语音…...
学习笔记041——Elastic Search的学习与使用以及SpringBoot整合
文章目录 1、Elastic Search介绍1.1、ES 的数据结构1.2、ES 为什么查询快1.3、CRUD 2、Spring Boot 整合 ES 1、Elastic Search介绍 Elasticsearch是一个分布式的、基于RESTful API的搜索和分析引擎,广泛用于大规模数据存储和快速检索。它最初由Shay Banon于20…...
R安装rgdal报错 解决办法
尝试了网上很多办法,不知道哪一步解决了,记录一下所有步骤: 1. 尝试github安装 options(repos c(CRAN "https://mirrors.tuna.tsinghua.edu.cn/CRAN/"))install.packages("devtools")library(devtools)devtools::in…...
【智能制造-46】人机工程(工厂自动化)
工作空间设计 设备布局规划 根据人体测量学数据,合理安排自动化设备、生产线和工作区域的布局。例如,考虑工人的操作空间和活动范围,确保他们能够舒适地接近和操作设备。在汽车装配车间,机器人和工人的工作区域应划分明确&#…...
C#笔记(5)
一、winform项目与窗体控件 1、部分类的使用 好处:让自动生成的代码后置,我们编写程序的代码显得更加简洁 特点:在最后编译的时候,仍然编译成一个窗体类。 窗体和控件的基本使用 3、Event事件(委托--》事件&#…...
【软件国产化】| Windows和Linux下文件名后缀是否区分大小写
今天在开发过程中遇到了个软件在Linux系统和Windows系统下功能表现不一致的bug,具体表现为: 插入一张图片(A文件夹中的001.jpg),然后使用“图片替换”功能,用B文件夹中的图片(B文件夹中的001.JP…...
讨论JAVA、JVM与Spring
Q1: 作为一个JAVA开发人员,对于jvm肯定不陌生,但很多人对它不陌生也仅止于概念上,而且对概念也是模糊不清的,但jvm实际是java程序运行在其中的实际存在的环境,对它的理解应该要是具象化的。 我们还是从一项技术产生的…...
【04】MySQL数据库和数据表的基本操作详解与实例
文章目录 一、连接MySQL服务器二、数据库的基本操作2.1数据库的基本操作1. 创建数据库2. 选择数据库3. 删除数据库4.查询所有数据库5.修改数据库的字符集 2.2 数据表的基本操作1. 创建数据表2. 查看数据表结构3. 删除数据表4. 修改数据表5. 插入数据6. 查询数据7. 更新数据8. 删…...
Spring中实现动态数据源切换,基于AbstractRoutingDataSource
背景 在项目开发过程中,我们可能会遇到一个场景:某个类型数据源有多个数据源实例,需要我们按照不同的请求切换到不同数据源去。 而目前绝大多数java应用都是基于Spring框架来开发,我们很多时候相关的数据源连接都是交给了Spring框…...
StarRocks-join优化
1、背景 有两个大表,都是6kw级别上下的,通过SR然后包装了一个接口对外提供查询,当前的问题是,这样大的join查询会导致BE直接宕机。并且这个sql很有代表性,我截图如下: 这个表是个单分区,所以直接…...
js 高亮文本中包含的关键词标红
在开发中,遇到需要将文本中包含的关键字高亮的情况,可以做以下处理。 <div class"title"v-html"highlightKeywords(item.title, state1.tags1.concat(state2.tags2).concat(state3.tags3))"> </div> ...... ...... con…...
DVWA靶场——File Inclusion
File Inclusion(文件包含)漏洞 指攻击者通过恶意构造输入,利用应用程序错误的文件包含机制,导致程序包含并执行未经授权的本地或远程文件。这类漏洞广泛存在于Web应用程序中,尤其是在那些允许用户提供文件路径或URL的地…...
Android Framework禁止弹出当前VOLTE不可用的提示窗口
文章目录 VoLTE简介VoLTE 的优势 当前VOLTE不可用的弹窗弹窗代码定位屏蔽弹出窗口 VoLTE简介 VoLTE(Voice over LTE)是一种基于4G LTE网络的语音通话技术。它允许用户在4G网络上进行高质量的语音通话和视频通话,而不需要回落到2G或3G网络。V…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

