5种经典I/O 通信模型
I/O 过程分析
对于一次 Network I/O (以 read 举例),它会涉及到两个系统对象,一个是调用这个 I/O 的进程或线程,另一个就是系统内核 (kernel)。当一个 read 操作发生时,会经历两个阶段(记住这两个阶段很重要,因为不同 I/O 模型的区别就是在两个阶段上各有不同的处理):
-
第一个阶段:等待数据准备 (Waiting for the data to be ready);
-
第二个阶段:将数据从内核拷贝到进程中 (Copying the data from the kernel to the process);
同步异步的区别在于调用结果的通知方式:
同步:同步调用发出去后,需要等待调用结果的通知后,才能进行后续操作
异步:异步调用发出去后,不用等待调用结果通知,可以继续后续操作
阻塞非阻塞区别在于线程/进程等待消息通知的时候,是挂起状态还是非挂起挂起状态
一:阻塞IO模型
阻塞 I/O 是最简单的 I/O 模型,一般表现为进程或线程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。
应用进程通过系统调用 recvfrom
接收数据,但由于内核还未准备好数据报,应用进程就会阻塞住,直到内核准备好数据报(此时文件描述符就会变为已就绪),然后由用户进程把数据由内存缓冲区复制到用户进程。整个过程在IO两端都会发生阻塞,recvfrom
完成数据报复制工作,应用进程才能结束阻塞状态。
如果在等待数据阶段发生阻塞,用户进程将不能接受其他连接
二.非阻塞IO模型
应用进程与内核交互,目的未达到之前,不再一味的等着,而是直接返回。简单解释就是等待数据阶段不再发生阻塞,然后通过轮询的方式,不停的去问内核数据准备有没有准备好。如果某一次轮询发现数据已经准备好了,那就把数据拷贝到用户空间中,只在O端发生阻塞。应用进程通过 recvfrom
调用不停的去和内核交互,直到内核准备好数据。如果没有准备好,内核会返回error
,应用进程在得到error
后,过一段时间再发送recvfrom
请求。在两次发送请求的时间段,进程可以先做别的事情。具体实现就是采用多线程的方式,不必因某一个线程的阻塞影响全部。
三.信号驱动IO模型
应用进程在读取文件时通知内核,如果某个 socket 的某个事件发生时,请向我发一个信号。在收到信号后,信号对应的处理函数会进行后续处理。应用进程预先向内核注册一个信号处理函数,然后用户进程返回,并且不阻塞,当内核数据准备就绪时会发送一个信号给进程,用户进程便在信号处理函数中开始把数据拷贝的用户空间中,也是只在O端阻塞。
四.IO复用模型
更详细的理解请参照:IO多路复用底层原理
多个进程的IO可以注册到同一个管道上,这个管道会统一和内核进行交互。当管道中的某一个请求需要的数据准备好之后,进程再把对应的数据拷贝到用户空间中。(就是只用这个管道进程去轮询了,不用像非阻塞模型,每个进程线程都去轮询),IO多路转接是多了一个select
函数,多个进程的IO可以注册到同一个select
上,当用户进程调用该select
,select
会监听所有注册好的IO,如果所有被监听的IO需要的数据都没有准备好时,select
调用进程会阻塞。当任意一个IO所需的数据准备好之后,select
调用就会返回,然后进程在通过recvfrom
来进行数据拷贝。
以上四种都是同步的,因为真正的数据拷贝过程都还是由用户进程来完成的,主要指O端从内核拷贝到用户空间,都是同步进行的。
五.异步IO模型
异步IO模型。应用进程把IO请求传给内核后,完全由内核去操作文件拷贝。内核完成相关操作后,会发信号告诉应用进程本次IO已经完成。(就是将数据从内核缓冲区拷贝到进程缓存区也是由内核完成,不是由用户进程来完成)
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/11335277.html