阻塞IO
为了完成IO操作,发起调用,调用结果返回之前则一直等待,直到完成,这个过程中为阻塞状态
在等待的过程中,什么都不做,直到条件具备才进行下一步操作
非阻塞IO
为了完成IO操作,发起调用,若不具备完成条件,则立即报错返回(通常需要循环操作)
当进程等待内核数据时,进程不断询问内核是否完成,直到内核数据准备完成,这个过程是一个轮询过程
非阻塞IO代码操作
#include<unistd.h>
#include<fcntl>
int fcntl(int fd,int cmd,.../* arg */);
//根据传入的cmd的值不同,后面追加的值也不同
fcntl中的函数功能
cmd = F_DUPFD | 复制一个现有的描述符 |
cmd = F_GETFD/F_SETFD | 获得/设置文件描述符标记 |
cmd = F_GETFL/F_SETFL | 获得/设置文件状态标记 |
cmd = F_GETOWN/F_SETOWN | 获得/设置异步IO所有权 |
cmd = F_GETLK/F_SETKW | 获得/设置记录锁 |
阻塞IO与非阻塞IO
阻塞IO与非阻塞IO关注的是程序在等待调用结果(消息,返回值)时的状态
信号驱动IO
为了完成IO操作,发起调用,直到自己完成功能之前该调用不会返回,当调用返回时,就会得到一个返回值,总的来说就是调用者主动等待这个调用的结果
- 优点:相较于非阻塞IO,更加实时,资源利用更充分
- 缺点:流程更加复杂,需要定义信号处理,既有主控流程也有信号处理流程,涉及到信号可靠问题
异步IO
为了完成IO操作,发起调用,调用发出之后立刻返回,所以收不到返回结果,也就是说当一个异步调用发出之后,调用者不会立刻得到结果,而是需要在调用发出之后,被调用者通过状态或者通知来告诉调用者
- 优点:对资源利用充分,以最高效率进行任务处理
- 缺点:资源消耗较高,流程较复杂
异步分类 - 异步阻塞:等待系统完成功能
* 异步非阻塞:不等待功能完成
同步与异步
描述的是功能是如何完成的,任务是如何处理
- 同步流程简单,占用资源少,但是效率没有异步高
- 异步处理效率高,占用资源多,但是流程复杂
多路转接IO
用来实现大量描述符的就绪事件监控,可以在程序中能够只针对就绪的描述符进行IO操作,提高效率,避免对没有就绪的描述符进行操作导致的阻塞,避免对大量没有就绪的描述符进行操作带来的效率降低
IO就绪事件:IO事件的就绪(可读/可写/异常)
- 可读事件:一个描述符当前是否有数据可读,
- 可写事件:一个描述符当前是否可以写入数据(一般对于socket来说)
- 异常事件:一个描述符是否发生了某些异常(比如连接断开,挂起之类)
附上一张图,便于理解
接下来通过例子来更形象的描述这几种IO操作
比如说我们去买衣服,首先我们先挑选衣服,衣服挑选完毕之后,等待店员把衣服装好.此时,等待店员把衣服装好就相当于内核准备数据,店员装衣服就相当于将数据从内核态复制到用户态,店员和服装店则是内核态进程,而我们就是用户态进程.
- 阻塞IO:就买一件衣服,然后开始等待,等待过程中什么事情也不做,等到衣服装好之后才离开
- ** 非阻塞IO** : 就买一件衣服,然后开始等待,这个过程中不去做其它事情,只是不停的去询问店员,衣服装好了吗,每等待一段时间就去询问一次,直到衣服装好才离开
- ** 信号驱动IO** : 买一件衣服,然后把手机号留给店员,告诉他当准备装衣服时就打电话告诉我,然后离开,等到电话来了,立刻回到服装店装好衣服再离开
- 异步IO: 买一件衣服,然后留下手机号,告诉他衣服装好了再打电话,然后离开,等到电话来了,回到服装店,再离开