看看八年前的(阻塞、非阻塞、多路复用、信号驱动、异步)IO总结

尊重原创,来自八年前的IO总结https://blog.csdn.net/historyasamirror/article/details/4270633

阻塞IO:

  • 执行过程

用户请求数据,系统内核(kernel)开始准备数据,用户进程被阻塞,数据被准备好后,kernel会将数据拷贝到用户内存,拷贝的过程中用户进程也被阻塞,直到kernel返回结果后,用户进程才解除阻塞,重新运行起来。

  • 解决办法:

在服务器端使用多进程或多线程,让每个连接都拥有独立的线程或进程。

  • 暴露的问题:

连接请求多的时候,占用系统资源,降低系统对外界响应速度。

  • 改进方案:

线程池:减少创建和销毁线程的频率
连接池:维持连接的缓存池,尽量重用已有的连接、减少创建和关闭连接的频率

  • 暴漏的问题:

池有上限,当请求超过上限,同样会出现问题。所以使用池,必须考虑其面临的响应规模和池的大小。

非阻塞IO:

  • 执行过程

当用户进程发出read操作时,进程并没有被阻塞,内核马上返回给进程响应,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以干点别的事情,然后再发起recvform系统调用。重复上面的过程,循环往复的进行recvform系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。

多路复用IO:

  • 执行过程

它的基本原理就是select/epoll这个函数会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程 ,select/epoll的好处就在于单个线程/进程,就可以同时处理多个网络连接的IO。如图:
在这里插入图片描述
当用户进程调用了select,那么整个进程会被阻塞,而同时,系统内核会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel系统内核拷贝到用户进程。

  • 与阻塞io的区别:

1、多路复用需要使用两个系统调用:select和recvfrom,而阻塞IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。
2、如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
3、在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,整个用户的process其实是一直被阻塞的。只不过process是被select这个函数阻塞的,而不是被socket IO给阻塞。

  • 暴漏的问题:

1、select()接口并不是实现“事件驱动”的最好选择。因为当需要探测的句柄值较大时,select()接口本身需要消耗大量时间去轮询各个句柄(select在后续博客写)。
2、该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。

信号驱动IO(signal driven IO)

  • 原理

首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它来读取数据报。无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达(第一阶段)期间,进程可以继续执行,不被阻塞。免去了select的阻塞与轮询,当有活跃套接字时,由注册的handler处理。

异步IO:

  • 执行过程

异步IO是真正非阻塞的,它不会对请求进程产生任何的阻塞,因此对高并发的网络服务器实现至关重要。
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从系统内核的角度,当它收到一个asynchronous(异步) read之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞。然后,系统内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel系统内核会给用户进程发送一个signal,告诉它read操作完成了。

synchronous(同步) IO和asynchronous(异步) IO的区别:

两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing(复用)都属于synchronous IO。有人可能会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作。non-blocking IO在执行recvfrom这个系统调用的时候,如果kernel的数据没有准备好,这时候不会block进程。但是当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内进程是被block的。而asynchronous IO则不一样,当进程发起IO操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。
asynchronous IO,它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

  • 举例:

A:阻塞IO,B:非阻塞IO,C:多路复用IO,D:异步IO,E:信号驱动IO,五个人在钓鱼:
A用的是最老式的鱼竿,所以呢,得一直守着,等到鱼上钩了再拉杆;
B的鱼竿有个功能,能够显示是否有鱼上钩,所以呢,B就和旁边的MM聊天,隔会再看看有没有鱼上钩,有的话就迅速拉杆;
C用的鱼竿和B差不多,鱼竿有显示是否上钩的功能,但他想了一个好办法,就是同时放好几根鱼竿(select/poll/epoll),然后守在旁边,一旦有显示说鱼上钩了,它就将对应的鱼竿拉起来;
D是个有钱人,干脆雇了一个人(kernel)帮他钓鱼,一旦那个人把鱼钓上来了,就给D发个短信,并送鱼上门;
E的鱼竿比较高级,鱼竿自带一个信号器,E不需要守着鱼竿,鱼上钩后,信号器自动给E发送短信通知,E过来取鱼。

猜你喜欢

转载自blog.csdn.net/u013034226/article/details/86179352