Linux中多路IO复用

首先要明白为什么要使用 多路IO复用

单进程/单线程要处理多个阻塞事件的时候会面临抉择,设置阻塞还是非阻塞呢?阻塞的话消息可能得不到及时的处理,就像排队买饭前边的饭卡丢了一堆人等他找饭卡,找到后才能接着打饭,非阻塞的话看似合理但是cpu不愿意了,大妈抡起大勺准备往你的碗里盛饭的时候你说饭卡找不到了然后大妈把饭缩回去,一个两个还好,如果大量饭卡丢了的话大妈就要发火了,实际上服务器中的连接大多数都是不活跃的,所以需要思考一种方法,让有饭卡的打饭,饭卡丢的一边呆着

简单的来说多路IO复用就是一个进程或线程同时完成多个文件描述符的监听,使用一个IO事件控制多个IO事件,它可以极大的提高进程/线程的并发性

在linux中多路IO复用可以使用select,poll,epoll来实现

select:
最早产生,甚至出现先于linux,提供便捷高效的监听,兼容性强
缺点:

  1. 轮询监听机制(效率低)
  2. 监听数量有限(1024个,大小可改但要重新编译内核)
  3. 只支持水平触发(高电平触发)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/time.h>#define PORT 8000
#define IP "192.168.1.41"struct sockaddr_in addr;/*
实现select水平触发下的消息回射
*/int init(){int lfd=socket(AF_INET,SOCK_STREAM,0);addr.sin_port=htons(PORT);addr.sin_family=AF_INET;inet_pton(AF_INET,IP,&addr.sin_addr);bind(lfd,(const struct sockaddr *)&addr,sizeof(addr));listen(lfd,64);return lfd;
}int main(){socklen_t len;char message[1024];int lfd=init();fd_set all,cur;FD_ZERO(&all);FD_ZERO(&cur);FD_SET(lfd,&all);int nfd=lfd+1;while(1){cur=all;int cnt=select(nfd,&cur,NULL,NULL,NULL);if(cnt<0){perror("select监听异常");exit(1);}if(FD_ISSET(lfd,&cur)&&cnt--){len=sizeof(addr);int cfd=accept(lfd,(struct sockaddr *)&addr,&len);if(cfd<0){perror("接收到异常的连接");exit(1);}FD_SET(cfd,&all);if(nfd<=cfd+1) nfd=cfd+1;cnt--;}for(int i=lfd+1;i<nfd&&cnt;i++){if(FD_ISSET(i,&cur)){int ret=read(i,message,sizeof(message));if(ret<0){perror("读取数据异常");exit(1);}else if(!ret){close(i);FD_CLR(i,&all);}write(i,message,ret);cnt--;}}}
}

poll:
出现时间晚于select
和select相比仅仅是摆脱了监听数量的限制,未能解决一些实际问题
问题:

  1. 轮询监听机制
  2. 用户态和内核态间的大量数据拷贝
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include <sys/time.h>#define MAX 1000
#define PORT 8000
#define IP "192.168.1.41"struct sockaddr_in addr;/*
实现poll水平触发下的消息回射
*/int init(){int lfd=socket(AF_INET,SOCK_STREAM,0);addr.sin_port=htons(PORT);addr.sin_family=AF_INET;inet_pton(AF_INET,IP,&addr.sin_addr);bind(lfd,(const struct sockaddr *)&addr,sizeof(addr));listen(lfd,64);return lfd;
}int main(){socklen_t len;char message[1024];int lfd=init();struct pollfd p[MAX];for(int i=0;i<MAX;i++) p[i].fd=-1;p[0].fd=lfd;p[0].events=POLLIN;int nfd=1;while(1){int cnt=poll(p,nfd,-1);if(cnt<0){perror("poll监听异常");exit(1);}if(p[0].revents&POLLIN&&cnt--){len=sizeof(addr);int cfd=accept(lfd,(struct sockaddr *)&addr,&len);if(cfd<0){perror("接收到异常的连接");exit(1);}for(int i=1;i<MAX;i++){if(p[i].fd==-1){p[i].fd=cfd;p[i].events=POLLIN;if(nfd<i+1) nfd=i+1;break;}}}for(int i=1;i<nfd&&cnt;i++){if(p[i].fd==-1) continue;if(p[i].revents&POLLIN){int ret=read(p[i].fd,message,sizeof(message));if(ret<0){perror("读取数据异常");exit(1);}else if(!ret){close(p[i].fd);p[i].fd=-1;p[i].events=0;}write(p[i].fd,message,ret);cnt--;}}}
}

epoll:
在内核中维护一个红黑树存储需要监听的文件描述符及其监听的事件,有事件触发时仅需要将目标文件描述符及事件拷贝到数组中传出即可
事件的触发和处理:

  • 当某个文件描述符上有感兴趣的事件发生时,触发事件,并将该文件描述符加入内核事件表的就绪队列中。

  • epoll 从就绪队列中获取已触发的文件描述符,根据红黑树找到对应的节点,并从节点中获取事件信息。

  • epoll 会将该文件描述符和触发的事件放入一个就绪事件列表中,以等待后续的处理。

优点:

  1. 监听数量没有限制
  2. 不需要用户态和内核态间进行大量的数据拷贝
  3. 不再使用轮询机制改用事件通知机制
  4. 支持水平触发和边沿触发

创建epoll时会有一个文件描述符维持红黑树的根节点,epoll使用结束的时候需要手动释放

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>#define PORT 8000
#define IP "192.168.1.41"struct sockaddr_in addr;/*
实现epoll水平触发下的消息回射
*/int init(){int lfd=socket(AF_INET,SOCK_STREAM,0);addr.sin_port=htons(PORT);addr.sin_family=AF_INET;inet_pton(AF_INET,IP,&addr.sin_addr);bind(lfd,(const struct sockaddr *)&addr,sizeof(addr));listen(lfd,64);return lfd;
}int main(){socklen_t len;char message[1024];int lfd=init();struct epoll_event event,events[1024];event.data.fd=lfd;event.events=EPOLLIN;int epoll=epoll_create(100);epoll_ctl(epoll,EPOLL_CTL_ADD,lfd,&event);while(1){int cnt=epoll_wait(epoll,events,sizeof(events)/sizeof(event),-1);if(cnt<0) {perror("epoll监听异常");exit(-1);}for(int i=0;i<cnt;i++){int fd=events[i].data.fd;if(fd==lfd){len=sizeof(addr);int cfd=accept(fd,(struct sockaddr *)&addr,&len);if(cfd<0) {perror("接收到异常的连接请求");exit(-1);}event.data.fd=cfd;event.events=EPOLLIN;epoll_ctl(epoll,EPOLL_CTL_ADD,cfd,&event);}else{int ret=read(fd,message,sizeof(message));if(ret<0) {perror("读取数据异常");exit(-1);}else if(!ret){close(fd);epoll_ctl(epoll,EPOLL_CTL_DEL,fd,NULL);}write(fd,message,ret);}}}
}

点击此处

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

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

相关文章

【MySQL】深入理解隔离性

深入理解隔离性 一、数据库并发的场景二、多版本并发控制&#xff08; MVCC &#xff09;三、三个前提知识1、3个记录隐藏字段2、undo日志 四、快照的概念五、Read View六、隔离级别RR与RC的本质区别 一、数据库并发的场景 数据库并发的场景总共有三种&#xff1a; 读-读&…

JVM中一次完整的GC回收流程

JVM堆内存结构简述 JVM堆内存结构图 堆初体验 所有的对象实例以及数组都要在堆上分配&#xff0c;堆是垃圾收集器管理的主要区域&#xff0c;也被称为“GC 堆”&#xff0c;也是我们优化最多考虑的地方。因为在一个项目中&#xff0c;会不断地创建对象&#xff0c;都是在堆里…

DevOps 教程 (4) - CI/CD 整合

在本第四章的"DevOps 教程"系列中&#xff0c;我们将介绍CI/CD整合的概念和实践。我们会介绍DevOps所带来的好处&#xff0c;包括团队协作、开发效率和产品交付速度的显著提升。 我们还将讨论在DevOps中的不同角色&#xff0c;并理解每个角色在持续集成和持续交付中的…

微调实操一: 增量预训练(Pretraining)

1、前言 《微调入门篇:大模型微调的理论学习》我们对大模型微调理论有了基本了解,这篇结合我们现实中常见的场景,进行大模型微调实操部分的了解和学习,之前我有写过类似的文章《实践篇:大模型微调增量预训练实践(二)》利用的MedicalGPT的源码在colab进行操作, 由于MedicalGPT代…

RAG +milvus示例

GitHub - NVIDIA/DeepLearningExamples: State-of-the-Art Deep Learning scripts organized by models - easy to train and deploy with reproducible accuracy and performance on enterprise-grade infrastructure. Towhee GitHub Zilliz GitHub

【交流】IGBT及驱动电路

最近要设计一款IGBT的驱动IC&#xff0c;学习了大部分的驱动电路。偶有心得&#xff0c;总结如下&#xff1a; 1、IGBT工作于大电流大电压的状态&#xff0c;这就要求其开关特性要好。尽量让IGBT工作在这种状态&#xff0c;I*V最小。换句话说&#xff0c;当有大电流时&#xf…

【LeetCode】216. 组合总和 III(中等)——代码随想录算法训练营Day25

题目链接&#xff1a;216. 组合总和 III 题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回…

浅压缩、深压缩、双引擎、计算机屏幕编码……何去何从?

专业视听领域尤其显示控制和坐席控制领域&#xff0c;最近几年最激动人心的技术&#xff0c;莫过于分布式了。 分布式从推出之日就备受关注&#xff1a;担心稳定性的&#xff0c;质疑同步性能的&#xff0c;怀疑画面质量的…… 诚然&#xff0c;我们在此前见多了带着马赛克的…

2024美赛数学建模问题D题思路模型分析——大湖水问题

问题 D: 大湖水问题 背景 美国和加拿大的五大湖是世界上最大的淡水湖群。这五个湖泊及其相连的水道构成了一个庞大的排水盆地&#xff0c;在这两个国家有许多大型城市&#xff0c;气候各异&#xff0c;局部天气条件各异。 湖泊的水被用于许多用途&#xff08;捕鱼、娱乐、发电…

【C++】类和对象1:类的定义、访问限定符、作用域及对象大小

前言 本文主要是简单的介绍一下类是什么、如何使用 类的定义 class className { // 类体&#xff1a;由成员函数和成员变量组成 };// 一定要注意后面的分号class为定义类的关键字&#xff0c;ClassName为类的名字&#xff0c;{}中为类的主体&#xff0c;注意类定义结束时后面…

智慧文旅:驱动文化与旅游融合发展的新动力

随着科技的快速发展和人们生活水平的提高&#xff0c;文化和旅游的融合成为了时代发展的必然趋势。智慧文旅作为这一趋势的引领者&#xff0c;通过先进的信息技术手段&#xff0c;推动文化与旅游的深度融合&#xff0c;为产业的发展注入新的活力。本文将深入探讨智慧文旅如何成…

【ASP.NET Core 基础知识】--身份验证和授权--使用Identity进行身份验证

一、Identity的基础知识 1.1 Identity的组成 在ASP.NET Core中&#xff0c;Identity是一个用于处理用户身份验证和授权的框架。它包含了一系列组件&#xff0c;用于管理用户、角色、声明等身份相关的功能。以下是ASP.NET Core Identity的主要组成部分&#xff1a; User Mana…

解决C#中无限递归导致的System.StackOverflowException异常

目录 背景&#xff1a; 错误示例分析: 为什么是错误的&#xff1f; 正确的使用递归&#xff1a; 修改后的代码&#xff1a; 原理和原因&#xff1a; 结论&#xff1a; 背景&#xff1a; 在软件开发中&#xff0c;递归是一种常见的编程技术&#xff0c;它允许方法调用自…

动态数码管实验

数码管动态显示原理 动态显示的特点是将所有数码管的段选线并联在一起&#xff0c;由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选&#xff0c;利用发光管的余辉和人眼视觉暂留作用&#xff0c;使人…

signalR+websocket:实现消息实时通讯——技能提升

signalR 解决步骤1&#xff1a;npm install microsoft/signalr6.0.6 安装指定版本的microsoft/signalr&#xff0c;我这边安装的版本是6.0.6 解决步骤2&#xff1a;引入import * as signalR from microsoft/signalr; import * as signalR from microsoft/signalr; 下面第三…

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏9(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言回收物品素材绘制UI代码控制垃圾桶回收功能效果 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中&#xff0c;我们将…

低成本高效益,电子画册才是品牌的重要选择

​随着互联网的普及和数字化技术的进步&#xff0c;电子画册已成为许多品牌的重要选择。与传统印刷画册相比&#xff0c;电子画册具有低成本、高效益的优点&#xff0c;成为品牌宣传的新趋势。 具体来说&#xff0c;电子画册可以通过在线平台或移动设备轻松查看&#xff0c;无需…

【Spring连载】使用Spring Data访问Redis(五)----Redis Cache

【Spring连载】使用Spring Data访问Redis&#xff08;五&#xff09;----Redis Cache 一、Redis Cache 过期1.1 Time-To-Live (TTL) 过期1.2 Time-To-Idle (TTI) 过期 Spring Data Redis在org.springframework.data.redis.cache包中提供了Spring框架缓存抽象&#xff08;Cache …

logback自定义生成DB日志(java环境)

目的&#xff1a; 未来在生成日志写入数据库中加一个特殊的字段&#xff0c;官方老版本提供的DBAppender无法实现&#xff0c;并且好巧不巧&#xff0c;在新版本这个实现也被删除了&#xff0c;所以重写一个实现。 1. 安装依赖 安装logback maven依赖 注意&#xff1a; lo…

数据结构——实验01-线性表的链式存储和操作

一、实验内容 二、算法思想与算法实现 1、解题思想 &#xff08;1&#xff09;逆序创建链表La就是使用头插法创建一个链表&#xff0c;所谓头插法就是在创建链表时始终将新元素插入到头结点之后&#xff0c;而正序创建链表Lb就是使用尾插法创建一个链表&#xff0c;所谓尾插法…