IO多路复用实现并发服务器

news2025/3/12 7:40:34

一.select函数

select 的调用注意事项
在使用 select 函数时,需要注意以下几个关键点:

1. 参数的修改与拷贝
   readfds 等参数是结果参数 :
   select 函数会直接修改传入的 fd_set(如 readfds、writefds 和 exceptfds)。
   为了保留原始监听集合,通常会定义一个备份集合(如 allread_fdset),并将它的拷贝传递给 select。
    示例:
    fd_set allread_fdset, readfds;
    FD_ZERO(&allread_fdset);
    FD_SET(fd1, &allread_fdset);
    FD_SET(fd2, &allread_fdset);

    readfds = allread_fdset; // 拷贝到临时集合
    select(..., &readfds, ...);
2. 计算 nfds

    nfds 是最大文件描述符值 + 1 :
    在新增监听句柄时,更新 nfds 较为简单。
    在减少监听句柄时,更新 nfds 较为复杂:
    如果需要精确计算,可以通过遍历或维护一个最大堆等数据结构来找到第二大的文件描述符。
    或者,可以选择忽略 nfds 的更新,但可能导致性能下降。
    
    
3. 超时参数 timeout
    timeout 的含义 :
    如果为 NULL,表示阻塞等待,直到有事件发生。
    如果指向的时间为 0,表示非阻塞模式。
    如果指定超时时间,则 select 会在超时后返回。
    注意:Linux 实现中,select 返回时会修改 timeout 为剩余时间 :
    如果需要重复使用 timeout,需要重新初始化。
    
4. 返回值的处理
    返回值的意义 :
    -1:表示错误。
    0:表示超时时间到,没有事件发生。
    正数:表示监听到的事件总数(包括可读、可写和异常事件)。
    优化事件处理 :
    可以利用返回值避免不必要的检查。例如,如果返回值为 1,并且已经在可读集合中处理了一个事件,则无需再检查可写和异常集合。

select 的缺点
    尽管 select 是一种经典的 I/O 多路复用机制,但它存在以下显著缺点:

    1. 文件描述符数量限制
        FD_SETSIZE 的限制 :
        每个 fd_set 最多只能监听 FD_SETSIZE 个文件描述符(在 Linux 上通常是 1024)。
        这一限制使得 select 不适合高并发场景。
        
    2. 遍历效率低
        需要逐一检查文件描述符 :
        返回的 fd_set 是一个位图,应用程序需要对所有监听的文件描述符逐一调用 FD_ISSET 来判断是否就绪。
        示例:

        for (int i = 0; i < nfds; i++) {
            if (FD_ISSET(i, &readfds)) {
                // 处理可读事件
            }
        }
3. nfds 的效率问题
    select 的实现方式 :
    select 内部会遍历从 0 到 nfds-1 的所有文件描述符,判断每个描述符是否是关心的,并检查是否有事件发生。
    即使只监听少数几个文件描述符(如 0 和 1000),select 仍然需要遍历 1001 个描述符,导致效率低下。


总结
优点
    简单易用,跨平台支持广泛。
缺点
    文件描述符数量受限 :最多只能监听 FD_SETSIZE 个文件描述符。
    遍历效率低         :需要逐一检查文件描述符,增加了开销。
    nfds 的问题        :即使监听的文件描述符稀疏分布,select 仍需遍历所有小于 nfds 的描述符。
这些缺点促使了更高效的 I/O 多路复用机制(如 poll 和 epoll)的出现,尤其是在高并发场景下,epoll 成为了更优的选择。

【1】管道select

【2】tcp服务器select

二.poll函数

poll 
   针对select 做了改进 
   底层实现 --- 用的是数组 
   poll --- 链表 
   poll 引入了事件机制 
   
   1. 遍历?
   2. poll 需要在 用户空间 和 内核空间 来回拷贝 

epoll 
   三种多路IO操作中最高效


   
 

三.epoll函数

3. epoll
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

struct epoll_event {
    __uint32_t events;      /* Epoll events */
    epoll_data_t data;      /* User data variable */
};
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);


epoll 解决了select和poll的几个性能上的缺陷:
① 不限制监听的描述符个数(poll也是),只受进程打开描述符总数的限制;
② 监听性能不随着监听描述 符数的增加而增加,是O(1) 的,
  不再是轮询描述符来探测事件,而是由描述符主动上报事件; //事件机制的 
③ 使用共享内存的方式,不在用户和内核之间反复传递监听的描述 符信息;
④ 返回参数中就是触发事件的列表,不用再遍历输入事件表查询各个事件是否被触发

------------------------------------------

epoll显著提高性能的前提是:
监听大量描述符,
并且每次触发事件的描述符文件非常少。
epoll的另外区别是:
①epoll创建了描述符,记得close;
②支持水平触发和边沿触发。

epoll使用注意事项:
//epoll_create
① int epoll_create(int size);    //创建epoll文件描述符
参数size并不是限制了epoll所能监听的描述符最大个数,
只是对内核初始分配内部数据结构的一个建议。
返回是epoll描述符。-1表示创建失败。


//epoll_ctl
② int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);    //epoll文件描述符的控制接口
功能:
     epoll_ctl控制对指定描述符fd执行op操作,event是与fd关联的监听事件。
参数:
   @epfd --- epoll对象 
   @op 
        op操作有三种:
        
        添加EPOLL_CTL_ADD,
        
        删除EPOLL_CTL_DEL,
        
        修改EPOLL_CTL_MOD。
        
    分别添加、删除和修改对fd的监听事件。
    重复添加fd会怎样(event相同或不相同):
    添加失败(errno:17, File exists)
    删除和修改不存在的fd会怎样:
    删除或修改失败(errno:9,Bad file descriptor)

  @fd -- 关心的fd 
  
  
event是与监听的fd相关联的事件信息,event->events描述了要监听的事件类型,有以下类型:
//事件类型:
EPOLLIN        可读
EPOLLOUT       可写
EPOLLRDHUP     套接口对端close或shutdown写,在ET模式下比较有用
EPOLLPRI       紧急数据可读
EPOLLERR       异常条件
EPOLLHUP       挂起,EPOLLERR和EPOLLHUP始终由epoll_wait监听,不需要用户设置
EPOLLET        边沿触发模式,在描述符状态跳变时才上报监听事件。(监听默认都是LT模式)(ET+非阻塞模式)
EPOLLONESHOT   只一次有效,设置oneshot标记,描述符在触发一次事件之后自动失效(fd还被监听),
               不会再上报任何事件,直到使用EPOLL_CTL_MOD重新激活,
               设置新的监听事件为止(可不可以和之前的事件一样?)。

event->data是个共用体,可以存放和fd绑定的描述符信息,
比如就存放描述符本身fd,或者一个结构体信息,包括fd,ip,port等等。

在epoll_wait返回时,只会返回一个event列表,需要从列表元素中获取fd等信息。
返回值:
        返回0表示控制成功,
        返回-1表示失败。

③ int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
//等待epfd上的io事件,最多返回maxevents个事件
timeout = -1 的行为是block;
timeout =  0 是立即返回

④ epoll监听ET事件时,fd必须是非阻塞套接口。
比如监听可读事件,当ET上报可读后,需要一直读fd直到遇到EAGAIN错误为止,以免遗留数据在缓冲区中。
如果fd是阻塞的,则会读到阻塞了。
EAGAIN错误对于非阻塞套接口来说不是错误,只是说没有数据可读或者没有空间可写。
EWOULDBLOCK就是EAGAIN,值都是11。
selset/poll/epoll的LT模式监听的fd可以是阻塞模式的。

⑤ 多路复用监听io事件时,如果对某个套接口监听可写事件,总是会返回可写而事实上可能没有数据要写。
处理方法:
①只有在有数据要写时才把要写的套接口加入 监听列表中,数据全部写完之后从监听列表中删除它;
②在有数据写时,首先尝试直接写,当直接写没有把数据全部写入发送缓冲区时再把这个套接口加入可写事件 监听列表。
(这种方式效率较高,需要套接口是非阻塞的,前一种方式可以是阻塞的吗?)
可以是阻塞的。

四. 特点和区别

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

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

相关文章

C++20 模块:告别头文件,迎接现代化的模块系统

文章目录 引言一、C20模块简介1.1 传统头文件的局限性1.2 模块的出现 二、模块的基本概念2.1 模块声明2.2 模块接口单元2.3 模块实现单元 三、模块的优势3.1 编译时间大幅减少3.2 更好的依赖管理3.3 命名空间隔离 四、如何使用C20模块4.1 编译器支持4.2 示例项目4.3 编译和运行…

有必要使用 Oracle 向量数据库吗?

向量数据库最主要的特点是让传统的只能基于具体值/关键字的数据检索&#xff0c;进化到了可以直接基于语义的数据检索。这在AI时代至关重要&#xff01; 回到标题问题&#xff1a;是否有必要使用 Oracle 向量数据库&#xff1f; 这实际还要取决于你的具体应用需求。 客观来讲…

小肥柴慢慢手写数据结构(C篇)(4-3 关于栈和队列的讨论)

小肥柴慢慢学习数据结构笔记&#xff08;C篇&#xff09;&#xff08;4-3 关于栈和队列的讨论&#xff09; 目录1 双端栈/队列2 栈与队列的相互转化2-1 栈转化成队列2-2 队列转化成栈 3 经典工程案例3-1 生产者和消费者模型&#xff08;再次重温环形缓冲区&#xff09;3-2 MapR…

java-单列模式-final-继承-多态

内存存储区域 引用变量和普通变量引用变量放在栈中&#xff0c;基本数据类型的内容是在堆内存中。 对象放在堆内存中&#xff0c;其引用变量放在栈中&#xff0c;指向堆内存存放对象的地址。 静态变量放在静态区中&#xff0c;静态变量在程序的执行始中中分配一次&#xff0c;…

基于杀伤链的勒索软件控制框架

40s说清楚勒索软件如何工作 基于杀伤链的勒索软件控制框架开发了4种缓解策略(预防、阻止、检测&响应、重建)&#xff0c;覆盖18个控制域90项控制措施&#xff0c;以正确管理与勒索软件攻击杀伤链各阶段相关的风险。 注&#xff1a;本文节选出自《基于杀伤链的勒索软件防御指…

Windows编程----结束进程

进程有启动就有终止&#xff0c;通过CreateProcess函数可以启动一个新的子进程&#xff0c;但是如何终结子进程呢&#xff1f;主要有四种方法&#xff1a; 通过主线程的入口函数&#xff08;main函数、WinMain函数&#xff09;的return关键字终止进程 一个应用程序只有一个入…

无标签数据增强+高效注意力GAN:基于CARLA的夜间车辆检测精度跃升

目录 一、摘要 二、引言 三、框架 四、方法 生成合成夜间数据 昼夜图像风格转换 针对夜间图像的无标签数据增强技术 五、Coovally AI模型训练与应用平台 六、实验 数据 图像风格转换 夜间车辆检测和分类 结论 论文题目&#xff1a;ENHANCING NIGHTTIME VEHICLE D…

SqlSugar 进阶之原生Sql操作与存储过程写法 【ORM框架】

系列文章目录 &#x1f380;&#x1f380;&#x1f380; .NET开源 ORM 框架 SqlSugar 系列 &#x1f380;&#x1f380;&#x1f380; 文章目录 系列文章目录一、前言 &#x1f343;二、用法介绍三、方法列表四、使用案例五、调用存储过程六、in参数用法七、SqlServer带Go的脚…

NO.33十六届蓝桥杯备战|函数|返回值|声明|调用|引用|函数重载(C++)

返回值 我们在设计的函数的时候&#xff0c;函数在经过计算后&#xff0c;有时候需要带回⼀些计算好的数据&#xff0c;这时候往往使⽤return 来返回&#xff0c;这⾥我们就讨论⼀下使⽤ return 返回。 return 后边可以是⼀个数值&#xff0c;也可以是⼀个表达式&#xff0c;…

5G工业路由器赋能无人码头,港口物流智能化管理

全球贸易发展促使港口需提升运营效率&#xff0c;传统港口面临诸多难题&#xff0c;无人码头成为转型关键方向。5G 工业路由器为其提供有力通信支持&#xff0c;引领港口物流变革。 随着无人码头建设在全球兴起&#xff0c;如荷兰鹿特丹港、中国上海洋山港等。码头作业设备需实…

【Academy】OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities

OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities 1. 什么是 OAuth&#xff1f;2. OAuth 2.0 是如何工作的&#xff1f;3. OAuth 授权类型3.1 OAuth 范围3.2 授权代码授权类型3.3 隐式授权类型 4. OAuth 身份验证4.1 识别 OAuth 身份验证4.2 侦察OAuth…

有关Java中的多线程

学习目标 ● 掌握线程相关概念 ● 掌握线程的基本使用 ● 掌握线程池的使用 ● 了解解决线程安全方式 1.为什么要学习线程? ● 从1946年2月14日世界上第一台计算机在美国宾夕法尼亚大学诞生到今天&#xff0c;计算和处理的模式早已从单用户单任务的串行模式发展到了多用户多…

【eNSP实战】配置交换机端口安全

拓扑图 目的&#xff1a;让交换机端口与主机mac绑定&#xff0c;防止私接主机。 主机PC配置不展示&#xff0c;按照图中配置即可。 开始配置之前&#xff0c;使用PC1 ping 一遍PC2、PC3、PC4、PC5&#xff0c;让交换机mac地址表刷新一下记录。 LSW1查看mac地址表 LSW1配置端…

LLMs基础学习(一)概念、模型分类、主流开源框架介绍以及模型的预训练任务

文章目录 LLM基础学习&#xff08;一&#xff09;一、大语言模型&#xff08;LLMs&#xff09;的简单介绍定义与基本信息核心特点局限性参考的模型 二、大语言模型&#xff08;LLMs&#xff09;名称后 “175B”“60B”“540B” 等数字的含义数字代表模型参数数量具体示例参数数…

软件IIC和硬件IIC的主要区别,用标准库举例!

学习交流792125321&#xff0c;欢迎一起加入讨论&#xff01; 在学习iic的时候&#xff0c;我们经常会遇到软件 IC和硬件 IC,它两到底有什么区别呢&#xff1f; 软件 IC&#xff08;模拟 IC&#xff09;和硬件 IC&#xff08;外设 IC&#xff09;是两种实现 IC 总线通信的方式…

4个 Vue 路由实现的过程

大家好&#xff0c;我是大澈&#xff01;一个喜欢结交朋友、喜欢编程技术和科技前沿的老程序员&#x1f468;&#x1f3fb;‍&#x1f4bb;&#xff0c;关注我&#xff0c;科技未来或许我能帮到你&#xff01; Vue 路由相信朋友们用的都很熟了&#xff0c;但是你知道 Vue 路由…

git文件过大导致gitea仓库镜像推送失败问题解决(push failed: context deadline exceeded)

问题描述&#xff1a; 今天发现gitea仓库推送到某个镜像仓库的操作几个月前已经报错终止推送了&#xff0c;报错如下&#xff1a; 首先翻译报错提示可知是因为git仓库大小超过1G限制。检查本地.git文件&#xff0c;发现.git文件大小已达到1.13G。确定是.git文件过大导致&…

怎么利用DeepSeek进行PCB设计?

最近在琢磨利用Deepseek改善PCB的细节设计&#xff0c;毕竟立创EDA里面没有集成DS&#xff0c;因此&#xff0c;如何让DS能识别图片成了重中之重。所幸最近腾讯元宝里面集成了R1的满血版&#xff0c;这个版本可以上传图片&#xff0c;于是让DS识别图片就可能了。 在原理图设计…

详细介绍 Jupyter nbconvert 工具及其用法:如何将 Notebook 转换为 Python 脚本

nbconvert 是 Jupyter 提供的一个非常强大的工具&#xff0c;允许用户将 Jupyter Notebook 文件&#xff08;.ipynb&#xff09;转换成多种格式&#xff0c;包括 Python 脚本&#xff08;.py&#xff09;、HTML、PDF、LaTeX 等。你可以通过命令行来运行 nbconvert&#xff0c;也…

windows上传uniapp打包的ipa文件到app store构建版本

uniapp是一个跨平台的框架&#xff0c;使用windows电脑也可以开发ios软件&#xff0c;因为uniapp的打包是在云端实现的&#xff0c;本地电脑无需使用mac电脑即可完成打包。 但是打包后的ipa文件需要上架到app store的构建版本上&#xff0c;没有mac电脑&#xff0c;又如何上架…