52 . 揭秘五种IO模型:钓鱼故事里的高效IO之道
🔥个人主页:Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
一.五种IO模型
我们常说IO慢,且在前面信号也进行了测试,但为什么比较慢呢?
其实是系统调用,读取和写入慢
如write read 如果缓冲区满了或者空了,那么你就无法写或者读,此时就需要一直等待
read()->Input -> 等待 + 拷贝
write() -> Output -> 等待 + 拷贝
所以 其实
IO = 等+ 拷贝
其中等的占比时间远远大于拷贝的
那么高效IO就是单位时间内,减少IO中等待的比重
1.1 引入例子理解
在正式讲之前,我们先用个钓鱼的例子,初步认识
钓鱼 = 等 + 拉(将🐟从水里拷贝到桶里)
在一个多🐟的湖里,张三前往钓鱼
其中张三是专注钓鱼的,鱼漂不动,张三不动 ---阻塞IO
李四也前往钓鱼,他看到张三就是只有鱼漂动了,张三才会动
李四则是钓鱼途中,一会玩下手机,一会看会书,一会看下鱼漂有没有动 --- 非阻塞IO
王五又前来了,不过与他们不同的是
王五在自己的钓竿上绑了一个铃铛,当鱼上钩,铃铛就会响,王五拷贝,否则王五则是专注干自己的事,不关注钓鱼 --- 信号驱动IO
赵六是村里的有钱人,也前来钓鱼,不同的是,他带了100条鱼竿来钓
如果 1号鱼竿的鱼漂动力,赵六就去1号鱼竿拷贝收🐟 33号动了就去33号拷贝收🐟 ---- 多路复用,多路转接
田七 是县里的有钱人,坐着司机小王开的车来的,且明确知道,自己是喜欢吃鱼,而非钓鱼,正当田七要去钓鱼时,公司打来了电话,请他去开会,因此田七就嘱托小王去钓鱼,不管他怎么钓,但钓到了就打电话通知他 ----异步IO
1.2 问题
问题1:阻塞vs非阻塞
阻塞会因为IO条件不具备,阻塞会卡住,直到条件就绪
非阻塞:检测到IO条件不具备,出错返回
不同:等待方式不同
我们常说,非阻塞IO效率高? 但这是错的
IO效率高本质是指单位时间内,IO中等待的比重小,而非阻塞只是做了更多的事情,并未降低比重
问题2:谁的钓鱼效率最高? 即IO效率
当然是赵六了,他有100条鱼竿,钓到的几率大,那么等待的占比就变小了
问题3:王五有没有等待?
王五也等待了,只是他不需要进行检测,但还是参与了钓鱼过程 拉🐟
如果王五不用等待,那么他为什么不直接走
结论 4
阻塞 非阻塞 信号驱动 多路复用 ---同步IO ---->只要参与了IO(等.拷贝),就是同步
问题5:同步IO vs 异步IO
IO=等+拷贝
凡是参与了等 拷贝的任意一步,就是同步 否则异步
1.3 正式认识

小结
任何IO过程中, 都包含两个步骤. 第⼀是等待, 第⼆是拷⻉. ⽽且在实际的应⽤场景中, 等待消耗的时间往往都远远⾼于拷⻉的时间. 让IO更⾼效, 最核⼼的办法就是让等待的时间尽量少.
1.4 高级IO重要概念
1.4.1 同步通信 vs 异步通信(synchronous communication/ asynchronous communication)
• 所谓同步,就是在发出⼀个调⽤时,在没有得到结果之前,该调⽤就不返回. 但是⼀旦调⽤返 回,就得到返回值了; 换句话说,就是由调⽤者主动等待这个调⽤的结果;• 异步则是相反,调⽤在发出之后,这个调⽤就直接返回了,所以没有返回结果; 换句话说,当⼀ 个异步过程调⽤发出后,调⽤者不会⽴刻得到结果; ⽽是在调⽤发出后,被调⽤者通过状态、通 知来通知调⽤者,或通过回调函数处理这个调⽤.
1.4.2 阻塞 vs ⾮阻塞
二. 非阻塞IO
2.1 fcntl
TL(2) Linux Programmer's Manual FCNTL(2)
NAME
fcntl - manipulate file descriptor
SYNOPSIS
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
代码实战,设计一个轮询的非阻塞IO
#include <iostream>
#include <unistd.h>
#include<fcntl.h>
#include<cstdio>
void SetNonBlock(int fd)
{
int fl=fcntl(fd,F_GETFL);//先获得fl,再进行设置
if(fl<0)
{
perror("fcntl");
return;
}
fcntl(fd,F_SETFL,fl| O_NONBLOCK);//说明是位图
}
int main()
{
SetNonBlock(0);
char buffer[1024];
while (true)
{
//Linux中,ctrl+d 标识输入结束,read返回为0 ,类似读取到文件结尾
ssize_t n = read(0, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n - 1] = 0; // 防止输出换行符
std::cout << buffer << std::endl;
}
else if(n<0)//非阻塞read 如果底层数据还没有准备好,算错误吗?不算,因为非阻塞IO应该有这样的打算
{
//1.读取错误 2. 数据还没准备好
if(errno==EAGAIN||errno==EWOULDBLOCK)
{
//数据还没准备好
std::cout<<"数据还没准备好"<<std::endl;
sleep(1);
//做自己的事情
continue;
}
else if(errno==EINTR)
{
continue;//系统调用被打断 :一个系统调用(System Call)在完成之前被一个信号(Signal)中断了。
}
}
else {
break;
}
sleep(1);
std::cout<<"."<<std::endl;//换行符我为了刷新c的缓冲区
}
return 0;
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)