T:2019/11/28 W:四 17:0:11 [HTML]: @TOC
前言
1) 什么是
I/O
? -I/O
输入/输出(Input/Output)
,分为IO
设备和IO
接口两个部分。2) Unix
和Linux
的区别? -UNIX
是一个功能强大、性能全面的多用户、多任务操作系统,可以应用从巨型计算机到普通PC
机等多种不同的平台上,是应用面最广、影响力最大的操作系统。(收费的商业软件) -Linux
是一种外观和性能与UNIX
相同或更好的操作系统,但,Linux
不源于任何版本的UNIX
的源代码,并不是UNIX
,而是一个类似于UNIX
的产品。(开源/免费的软件) 3) 什么是I/O
操作,什么是文件描述? -unix(like)
世界里,一切皆文件,而文件是什么呢?文件就是一串二进制流而已,不管socket
,还是FIFO
、管道、终端,一切都是文件,一切都是流。在信息 交换的过程中,我们对这些流进行数据的收发操作,简称为I/O
操作(input and output)
,从数据流中读取数据,系统会调用read
(读取数据);写入数据,系统调用write
(写入数据)。不过话说回来了 ,计算机里有这么多的流,我怎么知道要操作哪个流呢?对,就是文件描述符,即通常所说的fd
,一个fd
就是一个整数,所以,对这个整数的操作,就是对这个文件(流)的操作。我们创建一个socket
,通过系统调用会,返回一个文件描述符,那么剩下对socket
的操作就会转化为对这个描述符的操作。不能不说这又是一种分层和抽象的思想。
I/O
分类
- 同步
IO
, 异步IO
, 阻塞IO
, 非阻塞IO
区别联系- 分析
- 实际上同步与异步是针对应用程序与内核的交互而言的。
- 同步过程中进程,触发
IO
操作并等待(也就是阻塞)或者轮询的去查看IO
操作(也就是非阻塞)是否完成。- 异步过程中进程触发
IO
操作以后,直接返回,做自己的事情,IO
交给内核来处理,完成后内核通知进程IO
完成。- 同步和异步对应用程序而言关注的是内核与进程中间的协作关系;
- 阻塞与非阻塞更关注的是单个进程的执行状态。
- 同步有阻塞和非阻塞之分,异步没有,异步一定是非阻塞的。
- 阻塞、非阻塞、多路
IO
复用,都是同步IO
,异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。- 真正的异步
IO
需要内核的深度参与。换句话说,只有用户线程在操作IO
的时候根本不去考虑IO
的执行全部都交给内核去完成,而自己只等待一个完成信号的时候,才是真正的异步IO
。所以,用一个子线程去轮询、去死循环,或者使用select
,poll
,epool
, 都不是异步。- 结论:
- 同步:执行一个操作之后,进程触发
IO
操作并等待(也就是我们说的阻塞)或者轮询的去查看IO
操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。- 异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。
- 阻塞:进程给内核传达一个任务之后,一直等待内核处理完成,然后才执行后面的操作。
- 非阻塞:进程给内核传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。
I/O
模型
- 阻塞
I/O
模型(BIO)
- 最广泛的模型是阻塞
I/O
模型,默认情况下,所有套接口都是阻塞的。 进程调用recvfrom
系统调用,整个过程是阻塞的,直到数据复制到进程缓冲区时才返回(当然,系统调用被中断也会返回)。
- 最广泛的模型是阻塞
- 非阻塞
I/O
模型(NIO)
- 当我们把一个套接口设置为非阻塞时,就是在告诉内核,当请求的
I/O
操作无法完成时,不要将进程睡眠,而是返回一个错误。当数据没有准备好时,内核立即返回EWOULDBLOCK
错误,第四次调用系统调用时,数据已经存在,这时将数据复制到进程缓冲区中。这其中有一个操作时轮询(polling)
。
- 当我们把一个套接口设置为非阻塞时,就是在告诉内核,当请求的
- 异步
I/O
模型(AIO)
- 进程发起
read
操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel
的角度,当它受到一个asynchronous read
之后,首先它会立刻返回,所以不会对用户进程产生任何block
。然后,kernel
会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel
会给用户进程发送一个signal
,告诉它read
操作完成了。 - 这个模型工作机制是:告诉内核启动某个操作,并让内核在整个操作(包括第二阶段,即将数据从内核拷贝到进程缓冲区中)完成后通知我们。
- 这种模型和前一种模型区别在于:信号驱动
I/O
是由内核通知我们何时可以启动一个I/O
操作,而异步I/O
模型是由内核通知我们I/O
操作何时完成。
- 进程发起
- 信号驱动
I/O
模型(signal driven I/O
,SIGIO)
- 首先我们允许套接口进行信号驱动
I/O
,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO
信号,可以在信号处理函数中调用I/O
操作函数处理数据。当数据报准备好读取时,内核就为该进程产生一个SIGIO
信号。我们随后既可以在信号处理函数中调用recvfrom
读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它来读取数据报。无论如何处理SIGIO
信号,这种模型的优势在于等待数据报到达(第一阶段)期间,进程可以继续执行,不被阻塞。免去了select
的阻塞与轮询,当有活跃套接字时,由注册的handler
处理。
- 首先我们允许套接口进行信号驱动
I/O
多路复用I/O
复用模型用到select
和poll
函数,这两个函数也会使进程阻塞,select
先阻塞,有活动套接字才返回,但是和阻塞I/O
不同的是,这两个函数可以同时阻塞多个I/O
操作,而且可以同时对多个读操作,多个写操作的I/O
函数进行检测,直到有数据可读或可写(就是监听多个socket)
。select
被调用后,进程会被阻塞,内核监视所有select
负责的socket
,当有任何一个socket
的数据准备好了,select
就会返回套接字可读,我们就可以调用recvfrom
处理数据。-
正因为阻塞
I/O
只能阻塞一个I/O
操作,而I/O
复用模型能够阻塞多个I/O
操作,所以才叫做多路复用。