I/O模型(I/O Models)
- 阻塞式I/O(blocking I/O)
- 非阻塞式I/O(nonblocking I/O)
- I/O多路复用(I/O multiplexing)
- 信号驱动I/O(signal driven I/O)
- 异步I/O(asynchronous I/O)
对于一个network I/O涉及两个系统对象,一个是调用I/O的进程(process),另一个是系统内核(kernel)。
下面用read操作来当做例子,当一个read操作发生时涉及的两个步骤:
- 等待内核将数据准备好
- 内核将准备好的数据拷贝到进程,也就是从kernel space拷贝到user space
对于一个socket的read操作,第一步通常是等待网络数据。当包到达时,会先拷贝到内核的内存中。然后第二步,把数据从内核的缓冲区拷贝到应用程序的缓冲区。
阻塞式I/O(blocking I/O)
姜太公钓鱼,愿者上钩。鱼竿丢下去,就等到鱼上来,啥事不干,啥事也干不了。
当应用程序调用recvfrom这个system call,kernel只有等到准备好数据,把数据拷贝到应用程序的缓冲区,才会返回。应用程序在调用recvfrom后,如果kernel没有准备好数据,会被“睡觉”。进程一直阻塞着,在呼叫recvfrom这个system call之后,直到kernel返回数据,或者错误。
blocking IO的特点就是在IO执行的两个阶段都被block了。
非阻塞式I/O(nonblocking I/O)
鱼竿下去,想看B站,看一会B站,看一下鱼钓到没。
非阻塞式I/O和阻塞式I/O很大的区别就在于,应用程序调用recvfrom,马上会有返回,如果kernel还没准备好数据,会返回错误EWOULDBLOCK。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。应用程序收到error的返回,会再呼叫recvfrom。如果kernel准备好数据,就会把数据拷贝到应用程序的缓存区,返回。
如果一个应用程序处于类似循环呼叫recvfrom这个操作,这个称为polling,轮询
。
这个模型比较浪费CPU时间。
注意,非阻塞式I/O算是同步I/O,虽然在呼叫recvfrom会立刻收到返回,但是在copy datagram那里是阻塞的,所以算是同步I/O
I/O复用(I/O Multiplexing Model)
一个鱼竿太少了,就买20根鱼竿,不断看哪根鱼竿钓到鱼了。
I/O复用,相比阻塞式I/O和非阻塞式I/O,会呼叫两个system call,一般由select,poll,epoll实现。这里用select先 当例子。
应用程序在呼叫select之后被阻塞,直到返回有data可以读。当select返回有data可以读,应用程序就呼叫recvfrom把data copy到应用程序的缓冲区。
相比较阻塞式I/O和非阻塞式I/O,这个模式没有优化什么,但是最大的优点是可以处理多条连接。
在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。
如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
信号I/O(Signal-Driven I/O Model)
鱼竿挂个铃,丢下去,等铃响了就收竿了。
异步I/O(Asynchronous I/O Model)
鱼竿丢下去,雇个小弟在那帮我看,钓到了,打电话告诉我鱼钓了,然后回去收鱼。
异步I/O根据POSIX标准定义。
应用程序呼叫aio_read(POSIX异步I/O方法用aio_或lio_开头)。系统会马上返回并且应用程序没有被阻塞,而是继续执行。等到kernel准备好数据,并且拷贝到应用程序的缓冲区后,会给应用程序数据一个signal(应用程序通常告诉kernel怎么通知),告诉应用程序read操作已经好了。
和信号驱动I/O不同的是,异步I/O的应用程序整个进程都不会被阻塞,kernel拷贝好数据后,通知应用程序。而信号驱动,只是在数据可读后通知应用程序,还要再呼叫recvfrom去让kernel拷贝数据。
比较
blocking I/O vs nonblocking I/O
blocking:阻塞式I/O,非阻塞式I/O,I/O复用,信号驱动I/O
nonblocking:异步I/O
blocking IO会一直block住对应的进程知道相应操作完成,而nonblocking IO会在kernel还在准备数据的时候就返回,继续执行。
asynchronous I/O vs synchronous I/O
synchronous:阻塞式I/O,非阻塞式I/O,I/O复用,信号驱动I/O
asynchronous :异步I/O
POSIX对这两个名词的定义是:
- 一个同步I/O操作会导致进程阻塞直到该I/O操作完成
A synchronous I/O operation causes the requesting process to be blocked until that
I/O operation completes. - 一个异步I/O操作不会导致进程被阻塞
An asynchronous I/O operation does not cause the requesting process to be blocked.
资料来源
<<UNIX Network Programming(Volume1,3rd)>> 6.2章