这篇博客开始我们Linux的最后一个章节--常见IO模型,在之前的博客当中我们讲述过Linux中基础的IO操作,欢迎大家去阅读。
我们通常指的IO操作便是数据的输入和输出,对应的具体操作过程我们可以将其分为两个步骤:等待IO就绪和数据拷贝,那么针对这两个方面我们设计出了一些相关的IO模型。
目录
1.阻塞IO
1.1内容
1.2优缺点
2.非阻塞IO
2.1内容
2.2优缺点
3.信号驱动IO
3.1内容
3.2优缺点
4.异步IO
4.1内容
4.2优缺点
5.多路转接模型
5.1内容
5.2优点
5.3实现
5.3.1接口介绍
5.3.2代码实现
1.阻塞IO
1.1内容
阻塞IO是指:发起IO操作之后,如果当前不具备IO操作的条件,则进程等待直到条件满足后再进行IO操作。
1.2优缺点
这样做有很明显的优点便是:实现流程简单;但是缺点同样也很突出,即:资源利用率低下,会占用进程,效率较低。
2.非阻塞IO
2.1内容
非阻塞IO是指:发起IO操作之后,如果当前具备IO操作的条件,则完成IO操作后返回,若不具备条件,则直接报错返回。可是这样简单进行判断的话,该怎么保证我们的IO操作可以被进行呢。程序中设计给出的答案是--循环,加入循环,对IO条件不断进行判断,直到执行。
2.2优缺点
这样操作的优点是:资源利用率高(不进行IO操作时,进程可以被分配完成其他任务);缺点也正是创造它优点的部分,即:需要进行循环操作,流程变的复杂,代码设计难度提x
值得注意的是,我们设计非阻塞IO的目的是:在等待IO条件满足时可以让进程完成其他任务,但这样的操作也会有一定的缺陷,那就是文件IO操作并不会是实时的了。也就是说,如果某一时间IO条件满足,但进程被分配的其他任务并没有完成,那么进程在完成该任务后,才会进行对应的IO操作。
3.信号驱动IO
3.1内容
信号驱动IO是指:定义IO信号的处理方式,IO就绪通过信号通知,打断进程当前操作,然后发起对应的IO调用。
3.2优缺点
信号驱动IO是在非阻塞IO的基础上进行设计的,是为了解决非阻塞IO无法确保实时处理IO操作的问题,因此它的优点也就呼之欲出,即:对资源利用更加充分,能更好的把控进程,并且对IO的操作是实时的;缺点也很明显:代码复杂度进一步提高,流程更加复杂。
4.异步IO
4.1内容
异步IO是指:发起异步IO操作,IO的等待以及数据的拷贝都有系统完成,完成之后通知进程。
4.2优缺点
异步IO的提出,让我们对资源的利用率达到的一定程度上的最高,我们只需要完成操作内容的通知以及操作结果的接收即可,并不需要去完成具体操作,如此设计的优点也就十分明了,即:对资源的利用率提升到了最高;缺点便是:对程序的复杂度也达到了最高。
5.多路转接模型
5.1内容
多路转接模型是一种高效的IO多路复用技术,使得单个进程能够处理多个IO事件,常用于高并发服务器技术的使用,让我们可以针对大量描述符进行IO就绪事件监控。
5.2优点
- 让进程能够针对就绪的描述符进行IO操作,提高了任务的处理效率;
- 避免进程因为对于未就绪描述符进行操作,而导致阻塞的情况。
5.3实现
多路转接模型的具体实现案例有很多,例如:select、poll和epoll等。我们针对select模型进行讲述,对于select模型而言,IO事件被分为三类:可读事件、可写事件和异常事件。它的主要流程思想是:
- 定义指定IO事件的描述符集合;
- 将需要对指定事件进行监控的描述符添加到指定集合当中;
- 将事件的描述符集合拷贝到内核当中,进行事件监控;
- 对集合中所有的描述符进行一次遍历,若没有就绪内容则将描述符挂到内核IO事件队列当中;
- 若监控过程中,某一描述符就绪了所要监控的事件,则唤醒进程的阻塞;
- 被唤醒后,select会再次将描述符集合遍历一遍,将集合中没有就绪的描述符移除;
- 上三步完成监控后,只需要判断那一个描述符在集合当中,则该描述符就就绪了一个事件;
- 于是进程可以根据就绪不同的事件对描述符进行不同的IO操作。
5.3.1接口介绍
Ⅰ、定义集合:
fd_set set;
本质上该集合是一个比特位图,默认拥有1024个比特位,取决于__FD_SETSIZE,因此select对描述符进行IO监控,存在有最大数量限制。
Ⅱ、初始化集合,将需要监控的描述符添加到集合当中:
void FD_ZERO(fd_set *set);//初始化清空集合
void FD_SET(int fd, fd_set *set);//将fd描述符添加到set集合当中
void FD_CLR(int fd, fd_set *set);//将fd描述符从set集合当中删除
Ⅲ、开始监听集合:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
其中,int nfds是所有需要监控集合中,最大的描述符+1,提高遍历效率;fd_set *readfds是可读事件描述符;fd_set *writefds是可写事件描述符, fd_set *exceptfds是异常事件描述符。
struct timeval该结构体的内容如下:
struct timeval{
time_t tv_sec;
time_t tv_usec;
};
它的主要作用是设置本次监听阻塞时长,NULL表示一致阻塞,直到有描述符就绪或被信号打断。
select返回值为:返回实际就绪的描述符事件个数;出错返回-1;监听超时返回0(没有描述符就绪)。
Ⅳ、判断描述符在集合中,判断描述符就绪在事件中:
int FD_ISSET(int fd, fd_set *set);//判断fd描述符,是否在set集合中
5.3.2代码实现
设计代码使用相应接口对多路转接模型的内容进行简单实现,最后得到结果如下: