Linux多路转接
文章目录
- IO模型
- 多路转接
- select 和 poll
- epoll
IO模型
在还在学习语言的阶段,C++里使用cin,或者是C使用scanf的时候,总是要等着我们输入数据才执行,这种IO是阻塞IO。下面是比较正式的说法。
阻塞IO: 在内核将数据准备好之前,系统调用会一直等待数据的获取数据的做法,就是阻塞IO。
所以网络套接字,在你没有自行设置的情况下,用的也是阻塞方式IO。
上面说的仿佛只有读取一种情况,那么写呢?
实际上写也是有一样的问题,内核有缓冲区的空间才写,没有缓冲区空间就一样得阻塞。
非阻塞IO:顾名思义,如果需求的数据,内核还没卓备好,那么操作系统就会直接返回。
在C语言里,如果你使用C接口的非阻塞IO,如果没收到数据系统调用就返回,那么 宏变量 errno就会被设置,其值是EWOULDBLOCK 错误码。
C在C11标准之后,C++11标准之后,都是支持线程安全的。
那么问题来了,究竟什么是IO呢?
等待数据 + 拷贝数据
我们发现不论是网络的套接字,亦或者是我们常用的自己的输入输出,其实无非都在等待一些数据,把这些数据拷贝进我们的内存交由程序处理。
多路转接
理解多路转接之前,我们先思考一个问题,
IO=等待+拷贝。
那我们该什么时候区拷贝呢?
一些比较经典的操作就是,轮询,信号。
所谓轮询就是,每当我需要数据,我就问问你,数据好了没,没有我就稍等一会再来接着问,知道数据好了我取走。
所谓信号就是,当你数据好了你来通知我,让我来取走数据。
我们知道,一个主机可以和其他多个主机建立TCP链接,也就是需要使用多个套接字。
那么每当有一个新的连接来临,我们不想中断我主线程的业务,但是新连接的数据收发也要管理。该如何呢? 其中一般想到的是,开多个线程。
开多线程固然是一种解决方案,其对于一般服务器负载也没问题。
那么有成千上万的连接来临呢?要知道创建新线程也是有开销的,根据我的Linux下的POSIX线程库正常创建线程(不重新设置栈大小等),那么每一个线程约需要10MiB的空间。
算下来4GiB的内存,用户一般有3GiB,那么就是说约莫只有300个线程的情况,显然算不上高并发。
因此就有一种IO模型,其处于非阻塞IO,你的每一个文件描述符(windows下叫文件句柄),都有一个中间者来给你管理,当这些句柄有数据来临时,他来通知你,告诉你改处理这些数据了。而这就是多路转接。
下面介绍Linux多路转接常用的函数,select,poll,和epoll
select 和 poll
int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout);void FD_CLR(int fd, fd_set *set);int FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);
fd_set # 文件描述符集的类型
注:select和poll在2.6版本之后用的较少,因为后来的机器内存都相对较大,同时主要因为有了epoll的出现,使之取代了select。
select维护了一个文件描述符集,FDS,其类型如上面的 fd_set,你可以用一些列函数接口来操作。当你有一个文件描述符是5号文件描述符,那么你就可以调用 FD_SET取设置入你的fd_set的数据类型里面。然后最后交给select帮你管理。
虽然答题过程如上输代码一言,但是其实际使用并不方便。原因也十分简单,select的思想处理其实是一种轮询的方式。、,这导致你每次都要设置文件描述符。
下面是一段示例代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>#define MAX_FD 10 // 最大文件描述符数量
#define BUFFER_SIZE 1024 // 读取缓冲区大小int main() {int fd[MAX_FD]; // 存储文件描述符的数组fd_set read_fds; // select的可读文件描述符集合int max_fd = 0; // 当前最大的文件描述符char buffer[BUFFER_SIZE]; // 读取数据的缓冲区int i, ret;// 初始化文件描述符,这里只是示例,实际情况可能是套接字for (i = 0; i < MAX_FD; i++) {fd[i] = -1; // 初始化为-1,表示未使用}// 假设我们监听标准输入(文件描述符0)fd[0] = 0;max_fd = 0; // 标准输入的文件描述符是0while (1) {// 清空fd集合FD_ZERO(&read_fds);// 将需要监听的文件描述符加入到fd集合for (i = 0; i <= max_fd; i++) {if (fd[i] != -1) {FD_SET(fd[i], &read_fds);}}// 设置超时时间,这里设置为永远等待struct timeval timeout;timeout.tv_sec = 10; // 10秒timeout.tv_usec = 0; // 0微秒// 调用selectret = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);if (ret == -1) {perror("select error");exit(EXIT_FAILURE);} else if (ret == 0) {printf("select timeout\n");continue;}// 检查哪个文件描述符可读for (i = 0; i <= max_fd; i++) {if (fd[i] != -1 && FD_ISSET(fd[i], &read_fds)) {// 这里处理文件描述符i的数据memset(buffer, 0, BUFFER_SIZE);ssize_t count = read(fd[i], buffer, BUFFER_SIZE - 1);if (count > 0) {printf("Read from fd %d: %s\n", fd[i], buffer);} else if (count == 0) {// EOF,可能需要关闭文件描述符close(fd[i]);fd[i] = -1;} else {// 读取错误perror("read error");}}}}return 0;
}
你会发现意见很让人觉得效率低且麻烦的事情,那就是select每一次都需要遍历。如同轮询一般,因为你放进去的select文件描述符发生事件时,select并不会告诉你具体是谁发生了,只知道在 FD_MAX(目前最大的文件描述符为止),有事件发生,这就显得麻烦且效率低下。不过因为select上限文件描述符大多数都是1024,也就是 FD_SETSIZE 宏。所以select的整体效率不算高,但是其适用于一些比较没有那么支持性能的机器。
总结:
1.每次调用select都需要把fd集合从用户态往内核态拷贝一次,而每次拷贝都需要通过系统调用进入内核态,且在内核也是遍历访问这个开销在fd很多时会很大
2.select支持的文件描述符数量太小了,默认是1024
3.select返回后,需要遍历文件描述符集合,来获取已经就绪的socket
4.select不支持O_NONBLOCK
5.每次对要用第三方数组,动不动就需要遍历,十分耗时
poll类似select,解决了文件描述符上限,同时解决了输入输出每次重置的问题。(也就是select每次都要传一个表进去,同时也要传出来。)
具体用法不多叙述,可以自行百度。
epoll
epoll整体设计理念相较于select就比较人性化,我们知道每当有数据来临的时候,目前许多OS采用的都是硬件中断,让CPU临时去被数据接受之后存储起来。比如你的键盘输入就是如此。
那么为什么不把每个文件描述符有数据需要处理时,都会有信号,那么既然如此我维护这份记录就行,因此epoll就是如此做的。每当一个进程调用epoll时,会创建一个红黑树,将你关心的文件描述符添加进去,每当有事件来临,他就去红黑树里面找关心了这个事件与否,然后如果发现时关心了的,那么就通过回调(ep_poll_callback )把这个节点给放到 另外的就绪队列上去,如此你就知道这个事件需要用了。
因此总结一下:使得epoll关心文件描述符的方法
调用epoll_create创建一个epoll句柄;
调用epoll_ctl, 将要监控的文件描述符进行注册;
调用epoll_wait, 等待文件描述符就绪;
具体调用可查询手册,下面是例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>#define MAX_EVENTS 10
#define PORT 8080int main() {int listen_sock, conn_sock, epfd;struct sockaddr_in serv_addr;struct epoll_event event;struct epoll_event events[MAX_EVENTS];int num_fds;// 创建监听socketlisten_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock == -1) {perror("socket");exit(EXIT_FAILURE);}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(PORT);// 绑定socketif (bind(listen_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {perror("bind");exit(EXIT_FAILURE);}// 监听socketif (listen(listen_sock, 5) == -1) {perror("listen");exit(EXIT_FAILURE);}// 创建epoll实例epfd = epoll_create1(0);if (epfd == -1) {perror("epoll_create");exit(EXIT_FAILURE);}// 添加监听socket到epoll实例event.data.fd = listen_sock;event.events = EPOLLIN;if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &event) == -1) {perror("epoll_ctl: listen_sock");exit(EXIT_FAILURE);}// 事件循环while (1) {num_fds = epoll_wait(epfd, events, MAX_EVENTS, -1);if (num_fds == -1) {perror("epoll_wait");exit(EXIT_FAILURE);}for (int i = 0; i < num_fds; i++) {if (events[i].data.fd == listen_sock) {// 处理新的连接conn_sock = accept(listen_sock, NULL, NULL);if (conn_sock == -1) {perror("accept");exit(EXIT_FAILURE);}printf("Accepted connection on fd %d\n", conn_sock);// 将新的连接添加到epoll实例event.data.fd = conn_sock;event.events = EPOLLIN | EPOLLET; // 边缘触发模式if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock, &event) == -1) {perror("epoll_ctl: conn_sock");exit(EXIT_FAILURE);}} else {// 处理已连接socket的数据if (events[i].events & EPOLLIN) {char buffer[1024];ssize_t count;count = read(events[i].data.fd, buffer, sizeof(buffer));if (count == -1) {perror("read");close(events[i].data.fd);} else if (count == 0) {// 连接关闭printf("Closed connection on fd %d\n", events[i].data.fd);close(events[i].data.fd);} else {// 处理读取到的数据printf("Read %zd bytes from fd %d\n", count, events[i].data.fd);// 这里可以将数据发送回去或者进行其他处理}}}}}close(listen_sock);return 0;
}
相关文章:

Linux多路转接
文章目录 IO模型多路转接select 和 pollepoll IO模型 在还在学习语言的阶段,C里使用cin,或者是C使用scanf的时候,总是要等着我们输入数据才执行,这种IO是阻塞IO。下面是比较正式的说法。 阻塞IO: 在内核将数据准备好之前…...

IDEA导入Maven项目的流程配置以常见问题解决
1. 前言 本文主要围绕着在IDEA中导入新Maven项目后的配置及常见问题解决来展开说说。相关的部分软件如下: IntelliJ IDEA 2021.1JDK 1.8Window 2. 导入Maven项目及配置 2.1 导入Maven项目 下面介绍了直接打开本地项目和导入git上的项目两种导入Maven方式。 1…...

【数据分析---- Pandas进阶指南:核心计算方法、缺失值处理及数据类型管理】
前言: 💞💞大家好,我是书生♡,本阶段和大家一起分享和探索数据分析,本篇文章主要讲述了:Pandas进阶指南:核心计算方法、缺失值处理及数据类型管理等等。欢迎大家一起探索讨论&#x…...

2024世界机器人大会将于8月21日至25日在京举行
2024年的世界机器人大会预定于8月21日至25日,在北京经济技术开发区的北人亦创国际会展中心隆重举办。 本届大会以“共育新质生产力 共享智能新未来”为核心主题,将汇聚来自全球超过300位的机器人行业专家、国际组织代表、杰出科学家以及企业家࿰…...

【Linux】lvm被删除或者lvm丢失了怎么办
模拟案例 接下来模拟lvm误删除如何恢复的案例: 模拟删除: 查看vg名: vgdisplayvgcfgrestore --list uniontechos #查看之前的操作 例如我删除的,现场没有删除就用最近的操作文件: 还原: vgcfgrestore…...

疫情防控管理系统
摘 要 由于当前疫情防控形势复杂,为做好学校疫情防控管理措施,根据上级防疫部门要求,为了学生的生命安全,要求学校加强疫情防控的管理。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生&#x…...

永久删除的Android 文件去哪了?在Android上恢复误删除的消息和照片方法?
丢失重要消息和照片可能是一种令人沮丧的经历,尤其是在您没有备份的情况下。但别担心,在本教程中,我们将指导您完成在Android设备上恢复已删除消息和照片的步骤。无论您是不小心删除了它们还是由于软件问题而消失了,这些步骤都可以…...

宠物服务小程序多生态转化
宠物服务如美容造型、医疗、看护寄养等有着不少需求,尤其是年轻人从宠物生活到饮食起居等面面俱到,往往不惜金钱给到较好的环境,如定时除虫、优质食物、玩具、检查身体、服饰; 近些年宠物服务店新开数量也较多,同行竞…...

今天细说一下工业制造行业MES系统
文章目录 前言什么是MES? 前言 最近几个月在做制造行业的MES系统开发,这类面向制造业的系统是今年做的第三个系统了,也算是了解较深的了,和一个之前转行做这一块的朋友聊了聊,他们集团要扩大规模,准备招ME…...

C++ 知识点(长期更新)
C++ 知识点 C/C++1. `cin`, `cin.get()`, `getchar()`, `getline()`, 和 `cin.getline()`的区别。2. 有关 cin >>3. 定义和声明的区别4. `union`、`struct`和`class`的区别5. 深拷贝 vs 浅拷贝6. new 和 malloc 的区别7. 被free回收的内存是立即返还给操作系统吗?为什么…...

Spring AI + 通义千问 入门学习
Spring AI 通义千问 入门学习 文章目录 Spring AI 通义千问 入门学习一,开发环境配置二,项目搭建2.1 pom文件2.2 配置文件 三,AI使用3.1 对话问答3.1.1 普通方式3.1.2 流方式 3.2 文字生成图片 最近AI很火,而Spring也出了Spring…...

38.【C语言】指针(重难点)(C)
目录: 8.const 修饰指针 *修饰普通变量 *修饰指针变量 9.指针运算 *指针或-整数 *指针-指针 *指针关系运算 往期推荐 承接上篇37.【C语言】指针(重难点)(B) 8.const 修饰指针 const 全称 constant adj.不变的 *修饰普通变量 #…...

Vue-05.指令-v-for
v-for 列表渲染,遍历容器的元素或者对象的属性 v-for“列表元素名 in 列表名” <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…...

自动驾驶的一些大白话讲解
无人驾驶牛逼吗?我来帮你祛魅【原理缺陷解析】_哔哩哔哩_bilibili 5分钟讲明白自动驾驶端到端,解释为什么华为智驾并不是遥遥领先 到底什么是端到端自动驾驶系统?为何我会说这是智能驾驶的弯道超车机会?我希望今天用5分钟的时间&…...

Python学习笔记--参数
目录 实参与形参 不定长参数 拆分参数列表 返回值 实参与形参 1. 定义函数时,带默认值的形参必须放在不带默认值的形参后面 下面程序的输出结果是( )。 def StudentInfo(country中国,name): print(%s,%s%(name,country)…...

刷题——大数加法
大数加法_牛客题霸_牛客网 string solve(string s, string t) {if(s.size() < t.size()) return solve(t, s);reverse(s.begin(), s.end());reverse(t.begin(), t.end());string ans;int d 0;//进位制for(int i0; i < s.size(); i){d s[i] - 0;//取得数字值if(i < …...

Pytorch人体姿态骨架生成图像
ControlNet是一个稳定扩散模型,可以复制构图和人体姿势。ControlNet解决了生成想要的确切姿势困难的问题。 Human Pose使用OpenPose检测关键点,如头部、肩膀、手的位置等。它适用于复制人类姿势,但不适用于其他细节,如服装、发型和…...

前端面试常考的HTML标签知识!!!
语义标签 标签名语义描述header网页头部网页的主要头部区域nav网页导航网页的导航链接区域footer网页底部网页的底部区域aside网页侧边栏网页的侧边栏区域section网页区块网页的独立区块 | article | 网页文章 | 网页的独立文章区域 | 字符实体 作用:在网页中显…...

Oracle触发器
Oracle触发器就是特定事件发生时自动执行的存储过程。 触发器基本使用 基本语法: create [or replace] trigger 触发器名称 alter | before | instead of [insert] [[or] update [of 列1,列2,...]] [[or] delete] on 表名 [referencing {OLD [as] old / NEW [as]…...

GPT-5:未来已来,我们如何共舞于智能新纪元?
GPT-5:未来已来,我们如何共舞于智能新纪元? 在科技日新月异的今天,人工智能(AI)的每一次飞跃都深刻地改变着人类社会的面貌。从AlphaGo击败围棋世界冠军,到GPT系列模型引领自然语言处理&#x…...

2024年6月 青少年机器人技术等级考试理论综合试卷(五级)
202406 青少年等级考试机器人理论真题五级 第 1 题 ESP32 for Arduino,通过引脚2读取按键开关的返回值,电路如下图所示,程序pinMode(2, mode);中,参数mode的值是?( ) A:INPUT B&…...

【Go】 HTTP编程3-路由httprouter
HttpRouter httprouter httprouter是第三方的库,不是go的标准库,使用命令 go get -u github.com/julienschmidt/httprouter ,下载该模块,-u表示如果已经下载但更新到最新版本Router 实现了http.Handler接口,为各种 re…...

Spring Boot 整合 SA-Token 使用详解
Spring Boot 整合 SA-Token 使用详解 在现代Web开发中,用户认证与授权是构建安全应用的基础。SA-Token是一个轻量级的Java权限认证框架,它以其简洁的API设计、高效的执行效率以及丰富的功能特性,成为了众多开发者在Spring Boot项目中实现用户…...

1.3 C 语言入门实战:从 Hello World 到基础语法解析
目录 1 程序的作用 2 初识 Hello World 2.1 新建项目 2.2 程序解释 3 printf 的作用 4 注释及其注意事项 4.1 单行注释 4.2 多行注释 4.3 嵌套错误 5 项目在磁盘上的位置 1 程序的作用 如下图所示,我们编写了一个可以做加法的程序,我们给程序…...

一文读懂什么是Go语言goroutine
1. 进程、线程和协程的区别 进程: 进程是具有一定独立功能的程序,进程是系统资源分配和调度的最小单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换…...

计算机毕业设计 农家乐管理平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...

Spring Boot优缺点
Spring Boot 是一款用于简化Spring应用开发的框架,它集成了大量常用的框架和工具,大大简化了Spring项目的配置和部署。下面是Spring Boot的优缺点: 优点: 简化配置:Spring Boot自动配置功能可以根据应用的依赖自动配…...

Android Studio中创建apk签名文件
本文以macOS中Android Studio 2021.1.1版本为例介绍创建apk签名文件的操作步骤: 1.启动Android Studio,并打开一个Android项目。 2.依次点击菜单:Build -> Generate Signed Bundle / APK...。 3.在弹出的"Generate Signed Bundle or …...

CRC32 JAVA C#实现
项目中用到CRC32进行校验得地方,需要用到C#和java进行对比: 一、C#实现: class CRC32Cls { protected ulong[] Crc32Table; //生成CRC32码表 public void GetCRC32Table() { ulong Crc; …...

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——5Webscoket节点的使用
本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——5Webscoket节点的使用 有了前面几篇文章的铺垫,现在已经可以实现我到手测试那一步的 1.解读usb_websocket_display.launch.py 首先进入这个目录/root/dev_ws/src/origincar/originca…...