今天分析Linux 环境网络 IO 模型原理:
一、同步和异步,阻塞和非阻塞
1、同步和异步
关注的是调用方是否主动获取结果
同步
:
同步的意思就是调用方需要主动等待结果的返回
异步
:
异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等。
2、阻塞和非阻塞
主要关注的是等待结果返回时调用方的状态
阻塞
:
是指结果返回之前,当前线程被挂起,不做任何事
非阻塞
:
是指结果在返回之前,线程可以做一些其他事,不会被挂起。
3、两者的组合
1.
同步阻塞
:
同步阻塞基本也是编程中最常见的模型,打个比方你去商店买衣服,你去了之后发现衣服卖完了,那你就在店里面一直等,期间不做任何事(
包括看手机
)
,等着商家进
货,直到有货为止,这个效率很低。
2.
同步非阻塞
:
同步非阻塞在编程中可以抽象为一个轮询模式,你去了商店之后,发现衣服卖完了,这个时候不需要傻傻的等着,你可以去其他地方比如奶茶店,买杯水,但是你还
是需要时不时的去商店问老板新衣服到了吗。
3.
异步阻塞
:
异步阻塞这个编程里面用的较少,有点类似你写了个线程池
,submit
然后马上 future.get()
,这样线程其实还是挂起的。有点像你去商店买衣服,这个时候发现衣服没有
了,这个时候你就给老板留给电话,说衣服到了就给我打电话,然后你就守着这个电话,一 直等着他响什么事也不做。这样感觉的确有点傻,所以这个模式用得比较少。
4.
异步非阻塞
:
异步非阻塞。好比你去商店买衣服,衣服没了,你只需要给老板说这是我的电话,衣服到了就打。然后你就随心所欲的去玩,也不用操心衣服什么时候到,衣服一到,
电话一响就可以去买衣服了。
二、Linux
下的五种
I/O
模型
![](https://img-blog.csdnimg.cn/20210215135508748.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
1、
阻塞
I/O
模型:
![](https://img-blog.csdnimg.cn/20210215135546248.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
应用程序调用一个
IO
函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….
数据准备好了,从内核拷贝到用户空间
,IO
函数返回成功指示。 当调用 recv()
函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用 recv()
函数时,未必用户空间就已经存在数据,那么此时
recv() 函数就会处于等待状态。
2、非阻塞
IO
模型
![](https://img-blog.csdnimg.cn/20210215135632445.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
我们把一个
SOCKET
接口设置为非阻塞就是告诉内核,当所请求的
I/O
操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的 I/O
操作函数将不断的测试数据是否已经
准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用 CPU
的时间。上述模型绝不被推荐。
3、IO
复用模型:
![](https://img-blog.csdnimg.cn/20210215135649529.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
简介:主要是
select
和
epoll
两个系统调用;对一个
IO
端口,两次调用,两次返回,比阻塞 IO
并没有什么优越性;关键是能实现同时对多个
IO
端口进行监听; I/O 复用模型会用到
select
、
poll
、
epoll
函数,这几个函数也会使进程阻塞,但是和阻塞 I/O
所不同的的,这两个函数可以同时阻塞多个
I/O
操作。而且可以同时对多个读操作,多个写操作的 I/O
函数进行检测,直到有数据可读或可写时,才真正调用
I/O
操作函数。 当用户进程调用了 select
,那么整个进程会被
block
;而同时,
kernel
会
“
监视
”
所有
select 负责的 socket
;当任何一个
socket
中的数据准备好了,
select
就会返回。这个时候,用户进
程再调用
read
操作,将数据从
kernel
拷贝到用户进程。这个图和 blocking IO
的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select
和
recvfrom)
,而
blocking IO
只调用了一个系统调用
(recvfrom)
。但是,用 select
的优势在于它可以同时处理多个
connection
。(多说一句:所以,如果处理的连接数不是很高的话,使用 select/epoll
的
web server
不一定比使用
multi-threading + blocking IO 的 web server
性能更好,可能延迟还更大。
select/epoll
的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
4、信号驱动
IO
简介:两次调用,两次返回;
![](https://img-blog.csdnimg.cn/20210215135712187.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
首先我们允许套接口进行信号驱动
I/O,
并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个 SIGIO
信号,可以在信号处理函数中调用
I/O
操作函
数处理数据。
5、异步
IO
模型
![](https://img-blog.csdnimg.cn/20210215135820975.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作
6、5 种
I/O
模型的比较
![](https://img-blog.csdnimg.cn/20210215135845618.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25hbmRhbzE1OA==,size_16,color_FFFFFF,t_70)
不同
I/O
模型的区别,其实主要在等待数据和数据复制这两个时间段不同,图形中已经表示得很清楚了。
到此,Linux 环境网络 IO 模型原理分析完成,下篇我们分析阻塞网络编程原理,敬请期待!