目录
- IO模型
- 阻塞与非阻塞
- 同步与异步
- 阻塞IO
- 非阻塞IO
- 信号驱动IO
- 多路复用IO
- 异步IO
IO模型
根据各自的特性不同,IO模型被分为阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO五类。
最主要的两个区别就是阻塞与非阻塞,同步与异步。
阻塞与非阻塞
阻塞与非阻塞最主要的区别就是程序在等待调用结果时的状态。
阻塞:为了完成一个功能发起调用,如果不具备完成功能的条件,则调用会一直等待
非阻塞:为了完成一个功能发起调用,如果不具备完成功能的条件,则立即报错返回
同步与异步
同步与异步最主要的区别就是功能完成的流程是否是顺序化的,且完成的是自身还是系统。
同步:功能完成的流程是顺序化的,并且功能由自身完成。
异步:功能完成的流程是不确定的,并且功能由系统完成。
阻塞IO
发起IO调用,如果不具备IO条件,则一直等待直到条件就绪。
优点:流程以及代码实现都非常简单,任务顺序操作。
缺点:任务处理效率较低,无法充分利用资源。
非阻塞IO
发起一个IO调用,如果不具备IO条件,则立即报错返回,继续执行其他命令。通过一个循环来不断发起IO请求,直到条件就绪。
优点:与阻塞IO相比较来说,利用了等待的时间去做了其他的事情,对资源的利用更加充分。
缺点:与阻塞IO对比,IO调用需要循环发起,流程更加复杂。并且如果IO条件就绪了,也要等待上一轮循环结束后进入当前循环,才能进行处理,这就导致了IO不够实时。
非阻塞IO可以通过fcntl函数设置描述符状态来实现
例如:
void SetNoBlock(int fd)
{
int flag = fcntl(fd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
}
信号驱动IO
自定义一个IO就绪的信号,当IO就绪时就发出这个信号。在没有收到信号时,可以继续处理其他事情,一旦收到信号,就会中断当前操作,来优先处理IO事件。
优点:相较于非阻塞IO,因为信号到来后就直接强行中断进行处理,更加实时。并且在没收到信号的时候可以执行其他工作,资源利用更加充分。
缺点:因为需要自定义信号,又要有主控流程也要有信号处理流程,并且还需要考虑信号是否可靠导致的事件丢失情况,流程会更加的复杂。
多路复用IO
用于对大量的IO事件进行监控,能够让用户只针对就绪了指定事件(可读、可写、异常) 的IO进行IO操作。只针对就绪的描述符进行操作,避免了阻塞,并且提高了效率。
在Linux下,操作系统提供了三种模型:select模型、poll模型、epoll模型。这三种模型的具体使用以及细节会放到下一篇博客中
根据不同的模型具体的优缺点也不一样。
异步IO
IO处理的顺序不确定,整个IO的过程(等待 + 数据拷贝)由操作系统来完成而并非用户。
流程:
- 自定义一个IO完成信号
- 发起异步调用后返回,此时用户可以继续处理其他事情
- 系统进行IO事件的等待以及数据拷贝
- IO完成后通过信号通知进程IO
优点:对资源的利用最为充分, 以最高的效率进行任务的处理
缺点:资源消耗较高, 流程最为复杂
上面的五种IO模型,从前往后处理的效率逐渐增加,对资源的利用也增加充分,但是流程也越来越复杂。