5种网络I/O模型
IO发生时涉及的对象和步骤:
对象:
对于一个网络IO,涉及到了两个对象,一个是进程,另一个是系统内核。当一个read发生时,会进行一下步骤
步骤:
数据的准备(即等待)
-
将数据从内核拷贝到应用进程中
几种I/O模型的区别一般就在以上两个阶段有所不同
下面介绍了5种不同的I/O模型:
阻塞IO
非阻塞IO
多路复用IO
信号驱动IO
异步IO
以上5中IO中,前4种为同步IO,第5种为异步IO
1. 阻塞IO
在linux中,默认的socket都是阻塞的
一个典型的读操作的流程大概是这样的:
当用户进程调用recv/recvfrom系统调用时,会检查内核接收缓冲区是否准备好了数据,对于网络IO 来说,很多时候数据在一开始的时候还没有到达,所以进程会阻塞等待,直到内核缓冲区数据准备好了(对UDP来说要收到一个完整的数据报),内核就把数据拷贝到用户缓冲区,然后内核返回结果。
所以,阻塞IO的特点就是在IO执行的两个阶段都被block了
2. 非阻塞IO
在linux中,socket可以通过参数被设置为非阻塞的
同样,一个典型的阻塞读操作的流程大是这样的:
当调用recv/recvfrom系统调用时,检查内核是否准备好了数据,如果没有就直接返回一个error,并 不会阻塞等待数据,用户可以循环调用recv/recvfrom,这样的话,如果内核一直没有准备好数据, 函数就会一直返回一个error,直到数据准备好,才会把数据拷贝到用户缓冲区,并返回
所以,用户进程其实是需要不断的主动询问内核数据好了没有。
3. 多路复用IO
系统提供select函数和epoll来实现多路复用输入/输出模型,select/epoll可以用单个进程来处理多个网络接口的IO,它的基本原理就是这两个函数会不断的轮询它所负责的所有socket,当有任何一个 socket收到数据就会返回通知用户,此时用户调用recv/recvfrom,将数据从内核拷贝到用户内存用户进程其实也是一直被阻塞的,但和阻塞IO不同,该方式是select函数进行阻塞,而阻塞IO是recv函数进行阻塞,另外,select的优势是可以同时处理多个连接
4. 信号驱动IO
使用信号驱动I/O时,当网络套接字可读后,内核通过发送SIGIO信号通知应用进程,于是应用可以开 始读取数据。该方式并不是异步I/O,因为实际读取数据到应用进程缓存的工作仍然是由应用自己负责的。
5. 异步IO
当用户进程发起一个read操作后,内核收到该read操作后,首先它会立刻返回,所以不会对用户进程 阻塞,然后它会等待数据的准备完成,再把数据拷贝到用户内存,完成之后,它会给用户进程发送一个信号,告诉用户进程read操作完成了
下面来思考这两个问题:
1.阻塞IO与非阻塞IO的区别:
调用阻塞IO的时候进程会一直阻塞直到数据准备好,并且拷贝完成才解除阻塞
而非阻塞IO不会阻塞对应进程,尽管数据还没准备好,也会立刻返回
2.同步IO与异步IO的区别:
两者的区别在于,同步IO在IO操作的时候会阻塞,这里的IO操作是真正意义上的IO操作,也就是非阻塞IO中的recv/recvfrom系统调用,当内核准备好数据后,recvfrom会将数据从内核拷贝到用户中,这段时间内,进程是被阻塞的。
而异步IO则不一样,当进程发起IO操作后,就直接返回,剩下的工作全部交给内核来完成,知道内核给用户进程发送一个信号告诉它IO完成,整个过程中进程没有被阻塞
非阻塞IO和异步IO:
在非阻塞IO中,虽然进程大部分时间都不会被阻塞i,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而异步IO则完全不同。它就像是用户进程将整个IO操作交给了他人(内核)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。
《完》