【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…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...
聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...

