博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux I/O模型
阅读量:4167 次
发布时间:2019-05-26

本文共 2766 字,大约阅读时间需要 9 分钟。

本篇博文主要介绍UNIX中五种I/O模型。主要参考了UNP

UNIX中的I/O模型分为一下五种:

  • 阻塞I/O (blocking I/O)
  • 非阻塞I/O (nonblocking I/O)
  • I/O复用 (I/O multiplexing)
  • 信号I/O (signal driven I/O)
  • 异步I/O (the POSIX aio_ functions)

对于任意一个I/O读取(input)操作,主要分为两个阶段

1. 等待数据准备就绪(Waiting for the data to be ready)
2. 将准备好的数据从内核拷贝到用户缓冲(Copying the data from the kernel to the process)

在网络编程中, 第一个阶段经常会使得读取进程进入等待状态,当数据就绪时,就会被拷贝到内核缓冲中。这两个阶段是理解后面关于非阻塞和异步的关键。


blocking IO

目前用的比较多的是阻塞I/O模型。默认情况下,所有的socket都是阻塞的。我们用下图来解释阻塞I/O模型:

这里写图片描述

当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

所以,blocking IO的特点就是在IO执行的两个阶段都被block了


nonblocking IO

linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:

这里写图片描述
从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。所以,用户进程其实是需要不断的主动询问kernel数据好了没有。
并且它的阻塞是发生在将数据从内核缓冲拷贝到用户进程的过程中(第二阶段),在数据准备时(第一阶段)是不会阻塞的


I/O multiplexing

I/O复用可以实现用一个进程监听多个套接口。主要的系统调用包括select和poll,但是由于他们的伸缩性不太好,现在用的比较多的是epoll。在监听多个套接口时,当任何一个套接口有数据可读,则会返回。从下面的示意图我们可以看到

这里写图片描述
select/poll/epoll在等待套接口变得可读时会阻塞。
它主要阻塞在第一各阶段。当他们返回时,说明数据已经准备就绪,可以调用recvfrom函数从内核中将数据拷贝到应用进程中了(
第二阶段)。可以看出,这里需要两个系统调用性能是比不上前面两种I/O模型的。它的主要优势在与可以监听多个套接口,如果监听的套接口比较少,前面两个模型的效率还会更高一些。


Asynchronous I/O

我们通过调用aio_read来进行异步读操作。同时向内核传递套接口描述符,应用缓冲指针,缓冲大小和读取成功时,如何通知进程等参数。下面是示意图:

这里写图片描述
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以
不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。


signal I/O

这里写图片描述
在signal I/O中。我们告知内核当数据准备就绪时通过信号通知进程。这个IO模型和非阻塞型IO有点类似,
他们都是不需要阻塞在等待数据准备这个阶段上(第一阶段),但是和非阻塞IO相比,信号IO不需要反复去检查数据是否准备就绪,因为数据准备就绪时内核会通过信号通知它
。不过在将数据从内核拷贝到进程缓冲区时,他们都是有阻塞的。


几种IO模型的比较

这里写图片描述

从这个模型的比较中,我们可以看出:blocking IO,nonblock IO, IO multiplexing,signal IO 这四种IO都是有阻塞的,并且他们的第二个阶段都是阻塞的。只是他们阻塞的阶段不同。但是异步IO是两个阶段都并不阻塞的。


同步I/O VS 异步 I/O

  1. Asynchronous I/O operation causes the requesting process to be blocked until that I/O operation complete
  2. Anasynchronous I/O operation does not cause the requesting process to be blocked

从UNP给出的定义中我们可以看到,同步的意思是:I/O操作会引起阻塞。这里的I/O操作是指两阶段中的第二个阶段(actual I/O operation),毕竟第一个阶段只是等待数据准备而已。

所以我们可以得出结论,blocking IO,nonblock IO, IO multiplexing,signal IO 这四种IO都是属于同步的。

最后,再举几个不是很恰当的例子来说明这四个IO Model(blocking ,nonnlocking, IO multilexing,异步IO):
有A,B,C,D四个人在钓鱼:
A用的是最老式的鱼竿,所以呢,得一直守着,等到鱼上钩了再拉杆;
B的鱼竿有个功能,能够显示是否有鱼上钩,所以呢,B就和旁边的MM聊天,隔会再看看有没有鱼上钩,有的话就迅速拉杆;
C用的鱼竿和B差不多,但他想了一个好办法,就是同时放好几根鱼竿,然后守在旁边,一旦有显示说鱼上钩了,它就将对应的鱼竿拉起来;
D是个有钱人,干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了,就给D发个短信。

这里第一阶段就是等鱼上钩,第二阶段就是把鱼钩拉起来。,将鱼钩拉上来才是 actual I/O operation,而在这个动作上,前面三个都是要阻塞的,所以前三种都是同步的模型。

你可能感兴趣的文章
嵌入式100题(018):在1G内存的计算机中能否malloc(1.2G)?为什么?
查看>>
嵌入式100题(019):指针与引用的相同和区别;如何相互转换?
查看>>
嵌入式100题(040):什么是三次握手
查看>>
嵌入式100题(037):Http1.1和Http1.0的区别
查看>>
嵌入式100题(038):HTTPS与HTTP的一些区别
查看>>
嵌入式100题(042):为什么服务端易受到SYN攻击?
查看>>
嵌入式100题(043):什么是四次挥手
查看>>
嵌入式100题(044):为什么客户端最后还要等待2MSL?
查看>>
嵌入式100题(045):为什么建立连接是三次握手,关闭连接确是四次挥手呢?...
查看>>
嵌入式100题(028):static的用法(定义和用途)
查看>>
嵌入式100题(027):char和int之间的转换
查看>>
嵌入式100题(029):const常量和#define的区别(编译阶段、安全性、内存占用等)...
查看>>
嵌入式100题(030):volatile作用和用法
查看>>
嵌入式100题(033):TCP、UDP的优缺点
查看>>
嵌入式100题(035):TCP为什么是可靠连接
查看>>
嵌入式100题(034):TCP UDP适用场景
查看>>
嵌入式100题(70):一个程序从开始运行到结束的完整过程(四个过程)
查看>>
嵌入式100题(71):什么是堆,栈,内存泄漏和内存溢出?
查看>>
嵌入式100题(73):死锁的原因、条件 创建一个死锁,以及如何预防
查看>>
嵌入式100题(74):硬链接与软链接的区别
查看>>