一、IO模型
1、IO模型分类
1.阻塞IO--------blocking IO
2.非阻塞IO------nonblocking IO
3. 多路复用IO------- multiplexing
4.信号驱动IO-------signal driven IO (工作中不会使用到,只是作为了解)
5.异步IO------- asynchronous IO
2、通常情况下IO默认操作分为两个阶段(默认都是阻塞IO)
1.准备等待数据阶段,相当于请求操作系统是否有数据发送过来(调用IO操作)。
2.将数据从内核缓存拷贝到进程缓存中。
二、不同的IO操作
1、阻塞IO(blocking io)
1.在Linux系统中默认情况下所有的socket都是阻塞IO,如图所示
2.阻塞IO工作原理():
当用户进程调用了recvfrom这个系统调用(相当于用户进程发送请求像操作系统要数据),kernel就开始了IO的第一个阶段:准备 数据。对于network io来说,很多时候数据在一开始还没有到达,这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个 进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除 block的状态,重新运行起来。所以,blocking IO的特点就是在IO执行的两个阶段都被block了。
2、非阻塞IO
1.在Linux操作系统可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子
2.非阻塞IO工作原理:
从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一 个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它 就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么 它马上就将数据拷贝到了用户内存,然后返回。所以,用户进程其实是需要不断的主动询问kernel数据好了没有。如果数据还没准备好,此 时会返回一个error。进程在接收到error后,可以干点别的事情,然后再发起recvform系统调用。重复上面的过程,循环往复的进行recvform 系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整 个过程,进程仍然是属于阻塞的状态。
3.代码原理:
3、IO多路复用
1、以上情况只能应用于一个server处理一个客户sock,如果想让一个server处理多个客户sock那就要用到IO多路复用
2、多路复用流程图:
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的 数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。这个图和blocking IO的图 其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一 个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection,在IO multiplexing Model中,实际中,对于 每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被 select这个函数block,而不是被socket IO给block。
3.代码实现原理:
4、利用selectors来实现IO的多路复用
5、注释:在windows下只支持python中的select阻塞,而在linux下是三者都支持。我们通过selectors模块python可以自动判断
使用什么阻塞方式。
6、各个IO模型比较图: