在五种IO模型中我会嵌入钓鱼的例子说明IO模型
任何I/O操作无非都是两个过程,第一个是等待,等待I/O操作是否具备条件,第二个则是拷贝,实际过程中,
为了完成一个功能发起调用,如果当前无法完成,就一直等待,等到其具备条件完成后返回。
阻塞I/O是最常见的I/O模型,例如所有的套接字,默认都是阻塞方式的。
钓鱼:我来钓鱼了,我今天的目标是钓一条鱼,所以当我钓到一条之后我就走了,其实我不光要的是结果,我还享受钓鱼的过程。
为了完成一个功能发起调用,即使当前无法完成也立即返回。
非阻塞的缺点在于如果当前我们需要完成某一功能,但是不具备条件立即返回了,我们需要不断去调用函数反复尝试是否具备读写条件了,这种方式称为轮询,这对CPU资源是一种极大的浪费。
钓鱼:我又来钓鱼了,我今天的目标还是是钓一条鱼,只是今天我想换一种方式钓鱼,我就扔下鱼竿,看看能不能有鱼咬钩,没有我就把鱼竿拿走了等会再来钓。
提前设置好对应功能的信号,当具备条件,就发送信号,接到信号后发起调用,完成I/O操作。
信号驱动I/O需要提前自定义信号处理方式。
钓鱼:还是我,又来钓鱼了,我今天的目标还是是钓一条鱼,我今天嫌钓鱼太累了,我先放下鱼竿,然后雇了一个小孩,这个小孩帮我看鱼竿,我就去一边玩炉石去了,钓到鱼了这个小孩就叫我了,我放下手机,把鱼拉上来。
为了完成一个功能发起调用,但是这个调用可能不具备条件,我也仍然返回,只是不同于非阻塞I/O的是,这次调用之后,当其I/O操作具备条件,那么I/O操作会继续执行,并且通过信号来通知进程I/O操作完成。
钓鱼:钓鱼真无聊,可是我还要钓啊,我今天的目标还是是钓一条鱼,不过今天钓鱼我懒得管了,雇个人帮我看吧,于是我就扔下鱼竿,找了个小孩帮我看,这个小孩就在这等,我还是玩我的炉石,当鱼咬钩了后,小孩就帮我把鱼捞上来了,然后才告诉我。
我们在发起调用之前并不知晓I/O操作是否具备条件,一旦调用可能无法立即返回,多路转接提供的几种模型帮助我们去监控所需要I/O操作的文件描述符,如果有文件就绪,就会返回通知,没有条件具备就会继续监控,直到有描述符就绪,这个过程是多路转接模型帮助我们监控,我们仍可以执行自己的代码,无需浪费资源一直等待。
多路转接和阻塞I/O类似,都是一直等待,但是最大的区别在于多路转接技术可以同时等待多个文件描述符的状态,而且无需用户等待,而是交给多路转接模型等待,当其具备读或写条件,就会做相应的处理,多路转接最大的目的就是将串行I/O并行化,提高I/O效率。
钓鱼:我发誓这是我最后一次钓鱼了,我今天的目标是钓很多鱼,我又雇了上次那个小孩,只不过今天看的鱼竿更多了,每当有鱼咬钩了,小孩就告诉我,我来把对应鱼竿的鱼捞上来。
阻塞和非阻塞是一种状态的形容,针对的是程序的状态,关注点在程序等待调用的时候的状态。
阻塞:阻塞指的是当调用结果返回之前,当前线程会被挂起,只有在得到结果才会返回。
非阻塞:不能立刻得到结果,线程也不会挂起,而是立即返回。
同步和异步关注的消息的通信机制。
同步:同步只有调用得到结果才返回,不得到结果就不返回,但是并不代表同步就是阻塞的,同步和阻塞不是一个概念。
异步:异步指的是当调用发起后,无论是否成功,我都返回,但是返回并没有结果,当这个调用发出后,通过信号或者其他方式通知调用者调用完成,或者通过回调函数处理调用。
注意:这里的同步不是同步和互斥的同步,那个同步指的是访问临界资源的时序性,这里指的是调用的机制。
同步和阻塞不是一个概念
首先,同步是等待调用 得到结果(不论是否完成IO操作,只要有结果就行) 就才会返回,看似和阻塞类似,但是阻塞是 完成调用(完成IO操作) 才返回,也就是说非阻塞也可以是同步,非阻塞调用发现当前不具备条件,返回一个-1,这个-1也是结果,同步只要得到结果就可以返回了,无论结果是否是我们想要的。
这篇文章只写非阻塞的设置方式,多路转接由于有select和epoll,另外写一篇博客讲。
接口
#include
#include
int fcntl(int fd, int cmd, .../* arg */);
// fd 文件描述符
// cmd 选项
// F_DUPFD 复制现有描述符
// F_GETFD或F_SETFD 获得/设置文件描述符标记
// F_GETFL或F_SETFL 获得/设置文件状态标记
// F_GETOWN或F_SETOWN 获得/设置异步IO所有权
// F_GETLK,F_SETLK或F_SETLKW 获得/设置记录锁
获取文件描述符的属性,设置非阻塞
void SetNonblock(int fd)
{
int flags;
if(flags = fcntl(fd, F_GETFL, 0) < 0)
{
perror("fcntl");
return -1;
}
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}