当前位置:网站首页>高效IO模型
高效IO模型
2022-08-04 03:05:00 【秃头宇】
文章目录
前言
IO过程总体分为两步,第一步等:接收端->接受缓冲区等待数据的递达,发送端->等待发送缓冲区有可用空间,将发送数据放入发送缓冲区;第二步拷贝:接收端->将递达的数据从内核空间拷贝到用户空间的接受缓冲区,发送端->将发送的数据从用户空间的发送缓冲区拷贝至内核缓冲区;
所以高效IO本质是单位时间内拷贝的频率很高,重点是缩短等待的时间
一、五种IO模型
1.1 阻塞IO
阻塞IO是最常见的IO模型(在内核将数据准备好之前,系统调用会一直等待,所有的套接字,默认都是阻塞方式)
阻塞:本质是由OS发起,由OS执行,进程状态R->(S or T or D),进入等待队列当中,数据就绪后,进程被操作系统唤醒,再去执行
recvfrom
1.2 非阻塞IO
如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK
非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对CPU来说是较大的浪费, 一般只有特定场景下才使用

非阻塞轮询的本质是:由用户发起,OS执行,做事件就绪(OS有数据)的检测工作
1.3 信号驱动IO
内核将数据准备好的时候,使用SIGIO信号通知应用程序进行IO操作
1.4 异步IO
由内核在数据拷贝完成时,通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)

1.5 IO多路转接
虽然从流程图上看起来和阻塞IO类似. 实际上最核心在于IO多路转接能够同时等待多个文件
描述符的就绪状态
二、同步通信与异步通信
同步与异步关注的是消息通信机制
- 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回,就得到返回值;换句话说,就是由调用者主动等待这个调用的结果;拷贝需要用户参与
- 异步则是相反,调用在发出之后,这个调用就直接返回了,所有没有返回结果;换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果;而是在调用发出后,被调用者通过状态、通知来通知调用者,或者通过回调函数处理这个调用。拷贝不需要用户参与,操作系统拷贝
这里的同步通信和进程之间的同步是完全不相干的概念
三、非阻塞IO
非阻塞IO可以open打开时设置flag为非阻塞,这里主要介绍打开后的fd如何设置为非阻塞
3.1 fcntl
一个文件描述符,默认都是阻塞IO,fcntl函数用于已经打开的fd
该函数是一个系统调用函数
man 2 fcntl
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl函数有5种功能:
- 复制一个现有的描述符 ---- cmd=F_DUPFD
- 获得/设置文件描述符标记–cmd=F_GETFD或F_SETFD
- 获得/设置文件状态标记-----cmd=F_GETFL或F_SETFL
- 获得/设置异步IO所有权-----cmd=F_GETOWN或F_SETOWN
- 获得/设置记录锁--------------cmd=F_GETLK,F_SETLK或F_SETLKW
传入的cmd的值不同,后面追加的参数也不相同
目前只使用第三种功能,获取/设置文件状态标记,就可以将一个文件描述符设置为非阻塞
3.2 实现函数SetNoBlock
基于fcntl,实现一个SetNoBlock函数,将文件描述符设置为非阻塞
void SetNoBlock(int fd){
int fl = fcntl(fd,F_GETFL); //获取fd的状态标记
if(fl < 0){
perror("fcntl");
return ;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK); //将fl状态标记设置为非阻塞
}
3.3 如何使用函数SetNoBlock
以轮询的方式读取标准输入
#include<iostream>
#include<unistd.h>
#include<fcntl.h>
bool SetNonBlock(int fd){
int fl = fcntl(fd,F_GETFL);
if(fl < 0){
std::cerr << "fcntl error" << std::endl;
return false;
}
fcntl(fd,F_SETFL, fl | O_NONBLOCK);
return true;
}
int main()
{
#define NUM 1024
SetNonBlock(0);
while(true){
char buffer[NUM];
ssize_t size = read(0,buffer,sizeof(buffer)-1);
if(size < 0){
//不一定是出错了,有可能是底层没有数据
if(errno == EAGAIN || errno == EWOULDBLOCK ){
std::cout << "底层的数据没有就绪,你在轮询检测一下,try again " << std::endl;
continue;
}
if(errno == EINTR){
std::cout << "底层数据就绪未知,被信号中断 " << std::endl;
continue;
}
else{
std::cerr << "read error: " << size << " errno: "<< errno << std::endl;
break;
}
}
buffer[size] = 0; //主动给字符串最后添加'\0'
std::cout << "#echo: " <<buffer << std::endl;
}
return 0;
}
如果以非阻塞读取数据时,如果数据没有就绪,read是以出错的形式返回
如何判断是read真的出错?or 底层数据没有就绪 or read被信号中断
- errno == EAGAIN || errno == EWOULDBLOCK 时,说明数据没有就绪
- errno == EINTR ,说明被信号中断
grep -ER 'EAGAIN | EWOULDBLOCK' /usr/include/ 查看宏定义

边栏推荐
- 查看mysql死锁语法
- LeetCode每日一题(2285. Maximum Total Importance of Roads)
- Flink原理流程图简单记录
- new Date将字符串转化成日期格式 兼容IE,ie8如何通过new Date将字符串转化成日期格式,js中如何进行字符串替换, replace() 方法详解
- 移动端响应式适配的方法
- keytool命令
- activiti流程执行过程中,数据库表的使用关系
- 深度学习(三)分类 理论部分
- 2022.8.3-----leetcode.899
- How many ways do you know about communication between multiple threads?
猜你喜欢
随机推荐
Detailed analysis of scaffolding content
STM8S-----option byte
Mockito单元测试
pytorch应用于MNIST手写字体识别
怎样提高网络数据安全性
融云「音视频架构实践」技术专场【内含完整PPT】
LeetCode每日一题(2285. Maximum Total Importance of Roads)
Simple record of Flink principle flow chart
基于Qt的目录统计QDirStat
0.1 前言
Countdown to 2 days, the "New Infrastructure of Cultural Digital Strategy and Ecological Construction of Cultural Art Chain" will kick off soon
WPE详细教程
【学习笔记之菜Dog学C】动态内存管理
DIY电工维修如何拆卸和安装开关面板插座
Good bosses, please ask the flink CDC oracle to Doris, found that the CPU is unusual, a run down
案例 | 重庆银行流动数据安全挑战及应对实践
STM8S project creation (STVD creation) --- use COSMIC to create a C language project
Ant - the design of the Select component using a custom icon (suffixIcon attribute) suffixes, click on the custom ICONS have no reaction, will not display the drop-down menu
织梦响应式酒店民宿住宿类网站织梦模板(自适应手机端)
仿牛客论坛项目梳理









