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…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
【Linux】使用1Panel 面板让服务器定时自动执行任务
服务器就是一台24小时开机的主机,相比自己家中不定时开关机的主机更适合完成定时任务,例如下载资源、备份上传,或者登录某个网站执行一些操作,只需要编写 脚本,然后让服务器定时来执行这个脚本就可以。 有很多方法实现…...
