基础编程学习快乐每一天
首页
留言
Siddim.com
当前位置:
首页
>
编程知识库
>
后端开发知识
>
请你谈谈关于IO同步、异步、阻塞、非阻塞的区别
请你谈谈关于IO同步、异步、阻塞、非阻塞的区别
阅读
1
2019-11-22
同步(
synchronous
)
IO
和异步(
asynchronous
)
IO
,阻塞(
blocking
)
IO
和非阻塞(
non
-
blocking
)
IO
分别是什么,到底有什么区别?
这个问题其实不同的人给出的答案都可能不同,比如
wiki
,就认为
asynchronous
IO
和
non
-
blocking
IO
是一个东西。这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(
context
)也不相同。所以,为了更好的回答这个问题,我先限定一下本文的上下文。
本文讨论的背景是
Linux
环境下的
network
IO
。
本文最重要的参考文献是
Richard
Stevens
的“
UNIX
®
Network
Programming
Volume
1
,
Third
Edition
:
The
Sockets
Networking
”,
6
.
2
节“
I
/
O
Models
”,
Stevens
在这节中详细说明了各种
IO
的特点和区别,如果英文够好的话,推荐直接阅读。
Stevens
的文风是有名的深入浅出,所以不用担心看不懂。本文中的流程图也是截取自参考文献。
Stevens
在文章中一共比较了五种
IO
Model
:
blocking
IO
nonblocking
IO
IO
multiplexing
signal
driven
IO
asynchronous
IO
由于
signal
driven
IO
在实际中并不常用,所以我这只提及剩下的四种
IO
Model
。
再说一下
IO
发生时涉及的对象和步骤。
对于一个
network
IO
(这里我们以
read
举例),它会涉及到两个系统对象,一个是调用这个
IO
的
process
(
or
thread
),另一个就是系统内核(
kernel
)。当一个
read
操作发生时,它会经历两个阶段:
等待数据准备 (
Waiting
for
the
data
to
be
ready
)
将数据从内核拷贝到进程中 (
Copying
the
data
from
the
kernel
to
the
process
)
记住这两点很重要,因为这些
IO
Model
的区别就是在两个阶段上各有不同的情况。
blocking IO
在
linux
中,默认情况下所有的
socket
都是
blocking
,一个典型的读操作流程大概是这样:
当用户进程调用了
recvfrom
这个系统调用,
kernel
就开始了
IO
的第一个阶段:准备数据。
对于
network
io
来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的
UDP
包),这个时候
kernel
就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。
当
kernel
一直等到数据准备好了,它就会将数据从
kernel
中拷贝到用户内存,然后
kernel
返回结果,用户进程才解除
block
的状态,重新运行起来。
所以,
blocking
IO
的特点就是在
IO
执行的两个阶段都被
block
了。
non-blocking IO
linux
下,可以通过设置
socket
使其变为
non
-
blocking
。当对一个
non
-
blocking
socket
执行读操作时,流程是这个样子:
从图中可以看出,当用户进程发出
read
操作时,如果
kernel
中的数据还没有准备好,那么它并不会
block
用户进程,而是立刻返回一个
error
。从用户进程角度讲 ,它发起一个
read
操作后,并不需要等待,而是马上就得到了一个结果。
用户进程判断结果是一个
error
时,它就知道数据还没有准备好,于是它可以再次发送
read
操作。一旦
kernel
中的数据准备好了,并且又再次收到了用户进程的
system
call
,那么它马上就将数据拷贝到了用户内存,然后返回。
所以,用户进程其实是需要不断的主动询问
kernel
数据好了没有。
IO multiplexing
IO
multiplexing
这个词可能有点陌生,但是如果我说
select
,
epoll
,大概就都能明白了。有些地方也称这种
IO
方式为
event
driven
IO
。我们都知道,
select
/
epoll
的好处就在于单个
process
就可以同时处理多个网络连接的
IO
。
它的基本原理就是
select
/
epoll
这个
function
会不断的轮询所负责的所有
socket
,当某个
socket
有数据到达了,就通知用户进程。它的流程如图:
当用户进程调用了
select
,那么整个进程会被
block
,而同时,
kernel
会“监视”所有
select
负责的
socket
,当任何一个
socket
中的数据准备好了,
select
就会返回。这个时候用户进程再调用
read
操作,将数据从
kernel
拷贝到用户进程。
这个图和
blocking
IO
的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个
system
call
(
select
和
recvfrom
),而
blocking
IO
只调用了一个
system
call
(
recvfrom
)。但是,用
select
的优势在于它可以同时处理多个
connection
。
多说一句。所以,如果处理的连接数不是很高的话,使用
select
/
epoll
的
web
server
不一定比使用
multi
-
threading
blocking
IO
的
web
server
性能更好,可能延迟还更大。
select
/
epoll
的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
在
IO
multiplexing
Model
中,实际中,对于每一个
socket
,一般都设置成为
non
-
blocking
,但是,如上图所示,整个用户的
process
其实是一直被
block
的。只不过
process
是被
select
这个函数
block
,而不是被
socket
IO
给
block
。
Asynchronous I/O
linux
下的
asynchronous
IO
其实用得很少。先看一下它的流程:
用户进程发起
read
操作之后,立刻就可以开始去做其它的事。而另一方面,从
kernel
的角度,当它受到一个
asynchronous
read
之后,首先它会立刻返回,所以不会对用户进程产生任何
block
。
然后,
kernel
会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,
kernel
会给用户进程发送一个
signal
,告诉它
read
操作完成了。
到目前为止,已经将四个
IO
Model
都介绍完了。现在回过头来回答最初的那几个问题:
blocking
和
non
-
blocking
的区别在哪,
synchronous
IO
和
asynchronous
IO
的区别在哪。
先回答最简单的这个:
blocking
vs
non
-
blocking
。前面的介绍中其实已经很明确的说明了这两者的区别。调用
blocking
IO
会一直
block
住对应的进程直到操作完成,而
non
-
blocking
IO
在
kernel
还准备数据的情况下会立刻返回。
在说明
synchronous
IO
和
asynchronous
IO
的区别之前,需要先给出两者的定义。
Stevens
给出的定义(其实是
POSIX
的定义)是这样子的:
A
synchronous
I
/
O
operation
causes
the
requesting
process
to
be
blocked
until
that
I
/
O
operation
completes
;
An
asynchronous
I
/
O
operation
does
not
cause
the
requesting
process
to
be
blocked
;
两者的区别就在于
synchronous
IO
做”
IO
operation
”的时候会将
process
阻塞。按照这个定义,之前所述的
blocking
IO
,
non
-
blocking
IO
,
IO
multiplexing
都属于
synchronous
IO
。
有人可能会说,
non
-
blocking
IO
并没有被
block
啊。这里有个非常“狡猾”的地方,定义中所指的”
IO
operation
”是指真实的
IO
操作,就是例子中的
recvfrom
这个
system
call
。
non
-
blocking
IO
在执行
recvfrom
这个
system
call
的时候,如果
kernel
的数据没有准备好,这时候不会
block
进程。
但是,当
kernel
中数据准备好的时候,
recvfrom
会将数据从
kernel
拷贝到用户内存中,这个时候进程是被
block
了,在这段时间内,进程是被
block
的。而
asynchronous
IO
则不一样,当进程发起
IO
操作之后,就直接返回再也不理睬了,直到
kernel
发送一个信号,告诉进程说
IO
完成。在这整个过程中,进程完全没有被
block
。
各个
IO
Model
的比较如图所示:
经过上面的介绍,会发现
non
-
blocking
IO
和
asynchronous
IO
的区别还是很明显的。在
non
-
blocking
IO
中,虽然进程大部分时间都不会被
block
,但是它仍然要求进程去主动的
check
,并且当数据准备完成以后,也需要进程主动的再次调用
recvfrom
来将数据拷贝到用户内存。
而
asynchronous
IO
则完全不同。它就像是用户进程将整个
IO
操作交给了他人(
kernel
)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查
IO
操作的状态,也不需要主动的去拷贝数据。
最后,再举几个不是很恰当的例子来说明这四个
IO
Model
:
有
A
,
B
,
C
,
D
四个人在钓鱼:
A
用的是最老式的鱼竿,所以呢,得一直守着,等到鱼上钩了再拉杆;
B
的鱼竿有个功能,能够显示是否有鱼上钩,所以呢,
B
就和旁边的
MM
聊天,隔会再看看有没有鱼上钩,有的话就迅速拉杆;
C
用的鱼竿和
B
差不多,但他想了一个好办法,就是同时放好几根鱼竿,然后守在旁边,一旦有显示说鱼上钩了,它就将对应的鱼竿拉起来;
D
是个有钱人,干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了,就给
D
发个短信。
出处:
https
://
dwz
.
cn
/
gg7wFJTh
最近三期
【
20
期】你知道为什么
HashMap
是线程不安全的吗?
【
21
期】你能说说
Java
中
Comparable
和
Comparator
的区别吗
【
22
期】为什么需要消息队列?使用消息队列有什么好处?
? ~
以上数据来源于网络,如有侵权,请联系删除。
上一篇:
为什么需要消息队列?使用消息队列有什么好处?
下一篇:
请你谈谈单例模式的优缺点,注意事项,使用场景
评论
(0)
提交
类别
基础编程学习
HTML
PHP
Python
编程知识库
后端开发知识
热门文章
Java并发中的同步容器与并发容器,你了解多少?
Innodb中的事务隔离级别和锁的关系,难倒一半面试者!
SpringBoot + minio实现分片上传、秒传、续传
面试官:你知道消息队列如何保证数据不丢失吗?
JAVA知识 Java8新特性
面试官:谈谈为什么要限流,有哪些限流方案?
说说动态代理与静态代理区别
面试官:思考Tomcat 类加载器为什么要违背双亲委派模型?
boot-admin 基于SpringBoot的后台权限管理系统,可作为脚手架,用于快速搭建项目
SpringBoot+Vue+App+硬件实现智能家居系统项目