IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步、同步IO

segmentfault 对应博文页面 https://segmentfault.com/a/1190000016359495
最近看到OVS用户态的代码,在接收内核态信息的时候,使用了Epoll多路复用机制,对其十分不解,于是从网上找了一些资料,学习了一下《UNIX网络变成卷1:套接字联网API》这本书对应的章节,网上虽然关于该主题的博文很多,并且讲解的很详细,但是在这里还是做一个学习笔记,记录一下自己的想法。

IO模型

在《UNIX网络变成卷1:套接字联网API》这本书中,提到了五种I/O模型,分别为:阻塞式I/O、非阻塞式I/O、I/O复用(Epoll、select都是一种I/O复用机制),信息驱动式I/O、异步I/O,下面具体的一一介绍。

阻塞式I/O模型

阻塞,顾名思义,当进程在等待数据时,若该数据一直没有产生,则该进程将一直等待,直到等待的数据产生为止,这个过程中进程的状态是阻塞的。

IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步、同步IO

如上图所示,在linux中,用户态进程调用recvfrom系统调用接收数据,当前内核中并没有准备好数据,该用户态进程将一直在此等待,不会进行其他的操作,待内核态准备好数据,将数据从内核态拷贝到用户空间内存,然后recvfrom返回成功的指示,此时用户态进行才解除阻塞的状态,处理收到的数据。

从上述过程可以看出,用户态接收内核态数据的时候,主要有两个过程:内核态获得数据-->将数据从内核态的内存空间中复制到用户态进程的缓冲区中

非阻塞式I/O模型

在非阻塞式I/O模型中,当进程等待内核的数据,而当该数据未到达的时候,进程会不断询问内核,直到内核准备好数据。

IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步、同步IO

如上图,用户态进程调用recvfrom接收数据,当前并没有数据报文产生,此时recvfrom返回EWOULDBLOCK,用户态进程会一直调用recvfrom询问内核,待内核准备好数据的时候,recvfrom才返回结果,之后用户态进程不再询问内核,待数据从内核复制到用户空间,用户态进程开始处理数据。

需要注意的是,recvfrom不是等待数据从内核态复制到用户态之后才返回信息,而是当数据准备好就返回,所以之后当数据从内核复制到用户空间中的这一段时间中,用户态进程是处于阻塞的状态的。

非阻塞式I/O模型,个人觉得这个名字可能有点混淆,并不是和阻塞式模型是完全对立的,不是说进程等不到数据,就去做别的事情,恰恰进程这个时候一直在原地等待数据的到来,与阻塞式模型不同的是,非阻塞相当于进程一直在敲门问“数据好了么,快给我”,然后房门后的人说“没有准备好,请稍后!”,这个过程是一种轮询的状态,而阻塞式是佛系的态度,敲了一次门,房门后的人没有给任何回应,于是就去睡觉,啥都不做,直到房门后的人做出响应叫醒他,进程才去做下一步动作。

I/O复用模型

在ovs的用户态源码里,就用到了I/O复用模型,在计算机网络里面,有很多关于“复用”的用法,比如多路复用,意思就是本来一条链路上一次只能传输一个数据流,如果要实现两个源之间多条数据流同时传输,那就得需要多条链路了,但是复用技术可以通过将一条链路划分频率,或者划分传输的时间,使得一条链路上可以同时传输多条数据流。

IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步、同步IO

套用到I/O复用模型上,可以对应到如下应用场景:如果一个进程需要等到多种不同的消息,那么一般的做法就是开启多条线程,每个线程接收一类消息,如果每个线程都是采用阻塞式I/O模型,那么每个线程在消息未产生的时候就会阻塞,也就是说在多线程中使用阻塞式I/O。I/O复用就是基于上述的场景中,无需采用多线程监听消息的方式,进程直接监听所有的消息类型,这其中就涉及到select、poll、epoll等不同的方法。

如上图所示,用户态进程采用select的方法,通过select可以等待多个不同类型的消息,如果其中有一个类型的消息准备好,则select会返回信息,然后用户态进程调用recvfrom接收数据。

可以将select复用机制看作是一个描述符集合的管理,进程通过向这个集合中放入不同的描述符,用来等待不同的消息产生,然后通过select统一的进行管理,让其可以同时等待这个集合中任意一个事件的产生。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxgjz.html