同步IO、异步IO以及五种网络IO模式
目录
一、同步IO和异步IO
二、五种网络IO模式
1、阻塞IO
2、非阻塞IO
3、IO多路复用
3.1、SELECT
3.2、POLL
3.3、EPOLL

一、同步IO和异步IO
场景1: 小明去打开水,而开水塔此时没有水,小明在现场一直等待开水到来,或者不断的轮询查看是否有开水,直到有开水取到水为止,这是同步IO的一种案例!
同步IO的特点:
同步IO指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪。
同步IO的执行者是IO操作的发起者。
同步IO需要发起者进行内核态到用户态的数据拷贝过程,所以这里必须阻塞场景2: 小明去打开水,而开水塔此时没有水,开水塔的阿姨叫小明把水壶放到现场,来水后会帮他打好水,并打电话叫他来取,这是异步IO的一种案例!
异步IO的特点:
异步IO是指用户进程触发I/O操作以后就立即返回,继续开始做自己的事情,而当I/O操作已经完成的时候会得到I/O完成的通知。
异步IO的执行者是内核线程,内核线程将数据从内核态拷贝到用户态,所以这里没有阻塞
二、五种网络IO模式
对于一次IO访问(以read为例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,会经历两个阶段:1、等待数据准备 2、将数据从内核拷贝到进程中linux系统产生了下面五种网络模式的方案:1、阻塞IO(blocking IO)2、非阻塞IO(nonblocking IO)3、IO多路复用(IO multiplexing)4、信号驱动IO(signal driven IO)不常用
5、异步IO (asynchronous IO)
1、阻塞IO
小明同学急用开水,打开水时发现开水龙头没水,他一直等待直到装满水然后离开。这一过程就可以看成是使用了阻塞IO模型,因为如果水龙头没有水,他也要等到有水并装满杯子才能离开去做别的事情。很显然,这种IO模型是同步的。
在linux 中,默认情况下所有的socket都是blocking IO, 一个典型的读操作流程:

2、非阻塞IO
小明同学又一次急用开水,打开水龙头后发现没有水,因为还有其它急事他马上离开了,过一会他又拿着杯子来看看……在中间离开的这些时间里,小明同学离开了装水现场(回到用户进程空间),可以做他自己的事情。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO。
当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
典型的非阻塞IO模型一般如下:

设置非阻塞常用方式:
方式一: 创建socket 时指定
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);方式二: 在使用前通过如下方式设定
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
3、IO多路复用
有一天,学校里面优化了热水的供应,增加了很多水龙头,这个时候小明同学再去装水,舍管阿姨告诉他这些水龙头都还没有水,你可以去忙别的了,等有水了告诉他。于是等啊等(select调用中),过了一会阿姨告诉他有水了。
这里有两种情况:
情况1: 阿姨只告诉来水了,但没有告诉小明是哪个水龙头来水了,要自己一个一个去尝试。(select/poll 场景)
情况2: 舍管阿姨会告诉小明同学哪几个水龙头有水了,小明同学不需要一个个打开看(epoll 场景)

当用户进程调用了select,那么整个进程就会被block,而同时,kernel会 “监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
所以,IO多路复用的特点是通过一种机制,一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入就绪状态,select()函数就可以返回。
这里需要使用两个system call(select 和 recvfrom),而blocking IO只调用了一个system call(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。
如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用mutil-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll 的优势并不是对于单个连接能处理得更好,而是在于能同时处理更多的连接。
3.1、SELECT
在一段指定的时间内,监听用户感兴趣的文件描述符上可读、可写和异常等事件。
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
nfds : 最大的文件描述符加1。
readfds: 用于检查可读的。
writefds:用于检查可写性。
exceptfds:用于检查异常的数据。
timeout:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。
timeval结构的定义:
struct timeval{
long tv_sec; // seconds
long tv_usec; // microseconds
}
返回值: >0 是已就绪的文件句柄的总数, =0 超时, <0 表示出错,错误: errno #include <sys/select.h>
int FD_ZERO(fd_set *fdset); //一个 fd_set类型变量的所有位都设为 0
int FD_CLR(int fd, fd_set *fdset); //清除某个位时可以使用
int FD_SET(int fd, fd_set *fd_set); //设置变量的某个位置位
int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位
经典案例:
服务器端 server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>int main()
{int server_sockfd, client_sockfd;int server_len, client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;int result;fd_set readfds, testfds;server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket server_address.sin_family = AF_INET;server_address.sin_addr.s_addr = htonl(INADDR_ANY);server_address.sin_port = htons(9000);server_len = sizeof(server_address);bind(server_sockfd, (struct sockaddr*)&server_address, server_len);listen(server_sockfd, 5); //监听队列最多容纳5个 FD_ZERO(&readfds);FD_SET(server_sockfd, &readfds);//将服务器端socket加入到集合中while (1){char ch;int fd;int nread;testfds = readfds;//将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量 printf("server waiting\n");/*无限期阻塞,并测试文件描述符变动 */result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, (struct timeval*)0); //FD_SETSIZE:系统默认的最大文件描述符if (result < 1){perror("server5");exit(1);}/*扫描所有的文件描述符*/for (fd = 0; fd < FD_SETSIZE; fd++){/*找到相关文件描述符*/if (FD_ISSET(fd, &testfds)){/*判断是否为服务器套接字,是则表示为客户请求连接。*/if (fd == server_sockfd){client_len = sizeof(client_address);client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_address, &client_len);FD_SET(client_sockfd, &readfds);//将客户端socket加入到集合中printf("adding client on fd %d\n", client_sockfd);}/*客户端socket中有数据请求时*/else{ioctl(fd, FIONREAD, &nread);//取得数据量交给nread/*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */if (nread == 0){close(fd);FD_CLR(fd, &readfds); //去掉关闭的fdprintf("removing client on fd %d\n", fd);}/*处理客户数据请求*/else{read(fd, &ch, 1);sleep(5);printf("serving client on fd %d\n", fd);ch++;write(fd, &ch, 1);}}}}}return 0;
}
服务器端 client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>int main()
{int client_sockfd;int len;struct sockaddr_in address;//服务器端网络地址结构体 int result;char ch = 'A';client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立客户端socket address.sin_family = AF_INET;address.sin_addr.s_addr = inet_addr("127.0.0.1");address.sin_port = htons(9000);len = sizeof(address);result = connect(client_sockfd, (struct sockaddr*)&address, len);if (result == -1){perror("oops: client2");exit(1);}//第一次读写write(client_sockfd, &ch, 1);read(client_sockfd, &ch, 1);printf("the first time: char from server = %c\n", ch);sleep(5);//第二次读写write(client_sockfd, &ch, 1);read(client_sockfd, &ch, 1);printf("the second time: char from server = %c\n", ch);close(client_sockfd);return 0;
}
3.2、POLL
和select 一样,如果没有事件发生,则进入休眠状态,如果在规定时间内有事件发生,则返回成功,规定时间过后仍然没有事件发生则返回失败。可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。
poll 和select 区别: select 有文件句柄上线设置,值为FD_SETSIZE,
而poll 理论上没有限制!#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);输入参数:fds://可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回struct pollfd {int fd; /*文件描述符 open打开的那个*/short events; /*请求的事件类型,监视驱动文件的事件掩码*/ POLLIN | POLLOUTshort revents; /*驱动文件实际返回的事件*/}nfds: //监测驱动文件的个数timeout://超时时间,单位是ms
事件类型events 可以为下列值:POLLIN 有数据可读POLLRDNORM 有普通数据可读,等效与POLLINPOLLPRI 有紧迫数据可读POLLOUT 写数据不会导致阻塞POLLER 指定的文件描述符发生错误POLLHUP 指定的文件描述符挂起事件POLLNVAL 无效的请求,打不开指定的文件描述符
返回值:有事件发生 返回revents域不为0的文件描述符个数超时:return 0失败:return -1 错误:errno
服务器端 server_poll.c
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>#define MAX_FD 8192
struct pollfd fds[MAX_FD];
int cur_max_fd = 0;int main()
{int server_sockfd, client_sockfd;int server_len, client_len;struct sockaddr_in server_address;struct sockaddr_in client_address;int result;//fd_set readfds, testfds;server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socketserver_address.sin_family = AF_INET;server_address.sin_addr.s_addr = htonl(INADDR_ANY);server_address.sin_port = htons(9000);server_len = sizeof(server_address);bind(server_sockfd, (struct sockaddr*)&server_address, server_len);listen(server_sockfd, 5); //监听队列最多容纳5个//FD_ZERO(&readfds);//FD_SET(server_sockfd, &readfds);//将服务器端socket加入到集合中fds[server_sockfd].fd = server_sockfd;fds[server_sockfd].events = POLLIN;fds[server_sockfd].revents = 0;if(cur_max_fd <= server_sockfd){cur_max_fd = server_sockfd + 1;}while (1){char ch;int i, fd;int nread;//testfds = readfds;//将需要监视的描述符集copy到select查询队列中,select会对其修改,所以一定要分开使用变量printf("server waiting\n");/*无限期阻塞,并测试文件描述符变动 */result = poll(fds, cur_max_fd, 1000);//result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, (struct timeval*)0); //FD_SETSIZE:系统默认的最大文件描述符if (result < 0){perror("server5");exit(1);}/*扫描所有的文件描述符*/for (i = 0; i < cur_max_fd; i++){/*找到相关文件描述符*/if (fds[i].revents){fd = fds[i].fd;/*判断是否为服务器套接字,是则表示为客户请求连接。*/if (fd == server_sockfd){client_len = sizeof(client_address);client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_address, &client_len);fds[client_sockfd].fd = client_sockfd;//将客户端socket加入到集合中fds[client_sockfd].events = POLLIN;fds[client_sockfd].revents = 0;if(cur_max_fd <= client_sockfd){cur_max_fd = client_sockfd + 1;}printf("adding client on fd %d\n", client_sockfd);//fds[server_sockfd].events = POLLIN;}/*客户端socket中有数据请求时*/else{//ioctl(fd, FIONREAD, &nread);//取得数据量交给nreadnread = read(fd, &ch, 1);/*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 */if (nread == 0){close(fd);memset(&fds[i], 0, sizeof(struct pollfd)); //去掉关闭的fdprintf("removing client on fd %d\n", fd);}/*处理客户数据请求*/else{//read(fds[fd].fd, &ch, 1);sleep(5);printf("serving client on fd %d, read: %c\n", fd, ch);ch++;write(fd, &ch, 1);//fds[fd].events = POLLIN;}}}}}return 0;
}
3.3、EPOLL
详情请看我另一边博客EPOLL详解:
Epoll——详解-CSDN博客
https://blog.csdn.net/m0_65635427/article/details/140405993?spm=1001.2014.3001.5501
相关文章:
同步IO、异步IO以及五种网络IO模式
目录 一、同步IO和异步IO 二、五种网络IO模式 1、阻塞IO 2、非阻塞IO 3、IO多路复用 3.1、SELECT 3.2、POLL 3.3、EPOLL 一、同步IO和异步IO 场景1: 小明去打开水,而开水塔此时没有水,小明在现场一直等待开水到来,或者不断…...
IP-Guard日志数据上传至 SYSLOG 服务器操作指南
一、功能简介 服务器支持把日志数据上传到 SYSLOG 服务器。 二、功能配置 2.1 数据目录移交设置 在服务器安装目录下 OServer3.ini 文件中,添加工具启动配置,配置五分钟内生效。 Path:设置移交目录路径,IPG 服务器会把收集完成的…...
线程安全(二)synchronized 的底层实现原理、锁升级、对象的内存结构
目录 一、基础使用1.1 不加锁的代码实现1.2 加锁的代码实现二、实现原理2.1 synchronized 简介2.2 对象监控器(Monitor)2.3 加锁过程第一步:判断 Owner 指向第二步:进入 EntryList 阻塞第三步:主动进入 WaitSet 等待三、锁升级3.1 对象的内存结构3.2 Mark Word 对象头3.3 …...
【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十四)-无人机操控关键绩效指标(KPI)框架
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...
数电基础 - 半导体存储
目录 一. 简介 一. 只读存储器 二. 可编程只读存储器 三. 可擦除的可编程只读存储器 四. 随机存储器 五. 存储器容量的扩展 六. 总结 一. 简介 半导体存储是数字电路中用于存储数据的重要组成部分。 半导体存储器主要分为两大类:随机存取存储器࿰…...
校园工会体育报名小程序的设计
管理员账户功能包括:系统首页,个人中心,赛事公告管理,球员管理,球队信息管理,比赛信息,比赛报名管理 微信端账号功能包括:系统首页,比赛信息,比赛报名&#…...
2024Datawhale AI夏令营---基于术语词典干预的机器翻译挑战赛--学习笔记
#Datawhale #NLP 1.背景介绍: 机器翻译(Machine Translation,简称MT)是自然语言处理领域的一个重要分支,其目标是将一种语言的文本自动转换为另一种语言的文本。机器翻译的发展可以追溯到20世纪50年代,经历…...
手机下载APP (uniapp/vue)
一、uniapp <template><view class"content"><view class"appName">{{ formData.appName }}</view><view class"appInfo">{{ formData.appInfo }}</view><image class"logo" :src"formDa…...
python数据可视化(5)——绘制饼图
课程学习来源:b站up:【蚂蚁学python】 【课程链接:【【数据可视化】Python数据图表可视化入门到实战】】 【课程资料链接:【链接】】 Python绘制饼图分析北京天气 饼图,是一个划分为几个扇形的圆形统计图表ÿ…...
实习随笔【iviews的Select实现‘与全部互斥’的多选】
在实习中,遇到了如下需求,要求如下: 上面提到了一个需求为,选择全部与选择一个或者多个互斥,我们来看一下如何解决 核心代码 监听value的变化,如果含有‘全部’,且数组长度>1,则删…...
网站架构核心要素
高性能 技术指标:响应时间、吞吐量、并发数 前端优化手段 页面布局:css在前,js在后通信数据量:数据尽量精简缓存:浏览器缓存、cdn异步:ajax 后端优化手段 缓存:反向代理、redis异步&#x…...
XML 解析异常问题解决
问题描述 The parser has encountered more than "64000" entity expansions in this document; this is the limit imposed by the JDK. 在运行 Java 应用程序时,出现了 XML 解析异常。具体表现为: 报错信息显示无法创建 StAX(S…...
C# 匿名方法、Lambda、Linq概念及联系
匿名方法、Lambda表达式与LINQ 匿名方法 概念: 匿名方法是没有名称的方法实现,通常与委托关联使用。它提供了一种在不创建独立命名方法的情况下编写代码块的方式。 语法: delegate void MyDelegate(string message);MyDelegate del dele…...
django ninja get not allowed 能用 put delete
遇到一个奇怪的问题,django-ninja 编写的 get post 方法不能使用 # 获取Material router.get(/material, responseList[MaterialSchemaOut]) paginate(MyPagination) def list_material(request, filters: Filters Query(...)):qs retrieve(request, Material, f…...
服务器操作集合
服务器使用PC作为代理访问外网 1、PC上启动代理,比如nginx 下载nginx:http://nginx.org/en/download.html 修改配置文件,在conf下: http {include mime.types;default_type application/octet-stream;sendfile o…...
论文阅读【时空+大模型】ST-LLM(MDM2024)
论文阅读【时空大模型】ST-LLM(MDM2024) 论文链接:Spatial-Temporal Large Language Model for Traffic Prediction 代码仓库:https://github.com/ChenxiLiu-HNU/ST-LLM 发表于MDM2024(Mobile Data Management…...
【linux基础】linux远程传输三种免交互方式
linux远程传输三种免交互方式 文章目录 linux远程传输三种免交互方式1、使用sshpass工具2、使用expect脚本来输入密码3、SSH 密钥对 1、使用sshpass工具 建立信任关系的做法是最方便和安全的做法,但是在有些场景下(比如远端的authorized_keys是不能随意更改的)&…...
MySQL篇:事务
1.四大特性 首先,事务的四大特性:ACID(原子性,一致性,隔离性,持久性) 在InnoDB引擎中,是怎么来保证这四个特性的呢? 持久性是通过 redo log (重做日志&…...
处理在 electron 中使用开启了懒加载的 el-image 后,窗口最大化或窗口尺寸变化后图片无法显示的问题
文章目录 1、问题描述2、详情动图3、解决思路4、解决方案5、效果展示 1、问题描述 在 electron 中使用 el-image 时,开启了懒加载后,发现只有当窗口滚动后,图片才会显示,即便图片已经处于窗口的可视区域。当拖动窗口使其尺寸变大…...
Electron 进程间通信
文章目录 渲染进程到主进程(单向)渲染进程到主进程(双向)主进程到渲染进程 (单向,可模拟双向) 渲染进程到主进程(单向) send (render 发送)on &a…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.
报错 找到package.json文件 找到这个修改成 "lint": "eslint --fix --ext .js,.vue src" 为elsint有配置结尾换行符,最后运行:npm run lint --fix...
