Linux软件编程之I/O模型与多路复用

一、前言

       工作大半年了,就在昨天,一个朋友突然说起多路复用,于是懵了。虽然还记得一些东西,比如select,比如epoll等。但要我清晰地解释多路复用是啥,却怎么也开不了口,显而易见,对于之前学习过的知识,已经要忘得差不多了。

       果然,知识要时常温故,才能不忘记。于是,才有了写这篇博客的想法。

       为了不钓大家胃口,我先在这里把多路复用的概念简要跟大家提一下,看完之后若有不懂,再继续往下看。

       以阻塞I/O举例,我们知道,常规的阻塞I/O模型只能阻塞一个I/O操作。那么,我们想要阻塞两个甚至更多的I/O该怎么办呢是,I/O复用模型应运而生。正因为I/O复用模型能够阻塞多个I/O操作,所以才有了多路复用的概念。

       本篇博客部分内容摘抄自 络,若侵害他人权益,请评论或者私信告之。

二、一切皆文件与I/O流操作

       谈起I/O模型,我们需要先知道,什么是I/O。

       我们都知道unix(like)世界里,一切皆文件。而文件是什么呢件其实就是一串二进制流,不管socket,还是FIFO、管道、终端,对我们来说,一切都是文件,一切都是流。

       在信息交换的过程中,我们都是对这些流进行数据的读写操作,简称为I/O操作(input and output)。往流中读出数据,系统调用read;写入数据,系统调用write。不过话说回来了 ,计算机里有这么多的流,我怎么知道要操作哪个流呢,就是文件描述符,即通常所说的fd。一个fd就是一个整数。所以,对这个整数的操作,就是对这个文件(流)的操作。

       若创建一个socket,通过系统调用会返回一个文件描述符,那么剩下对socket的操作就会转化为对这个描述符的操作。

三、I/O模型

       在《Unix 络编程》一书中提到了五种IO模型,分别是:阻塞IO、非阻塞IO、多路复用IO、信 驱动IO以及异步IO。

3.1、阻塞IO与非阻塞IO

       谈起阻塞这个概念,理解起来应该不算太难,举个简单的例子。

       奶茶,大家都爱喝,但是我们都知道,通常奶茶店都有很多人,但是我们又想要喝。此时,我们就有两种选择:一种是选择到店铺里面排队,一直在那里等,直到轮到自己为止,在此期间,自己哪里都去不了,不能去做其他事,这就是所谓的阻塞;另一种选择是点外卖,因为想要喝到奶茶,所以隔一会儿就通过外卖软件去确认外卖的位置,不过其他时间可以用来做其他的事情,这就是所谓的非阻塞。

       Linux下,所有的系统IO调用都是阻塞式的,若想实现非阻塞,那就必须加入适当的参数。

       比如open函数,他有三个参数,分别是:文件路径、打开方式和文件权限,它默认是阻塞的,若想实现非阻塞,那么就需要在打开方式这里或上“O_NONBLOCK”,这样我们就能以非阻塞的方式打开文件了。其他很多函数同理。

3.2、多路复用IO

       在前言中,我们已经知道了多路复用IO的概念了,那么现在来具体讲解一下怎么实现多路复用。

       在Linux的系统调用中,比如read、open等函数,默认都是阻塞式的,而且针对的对象,只能有一个。而如果我们想要进行 络编程,在面对 络编程中极其庞大的数据并发时,若是还用只能针对一个对象的I/O操作,恐怕有多少个对象就需要创建多少个线程去处理才行。但实际上,我们并不能这么做。而这时候,可能就会想,要是有一个能够同时监控多个对象的I/O操作就太棒了。很好,有了这样的思维,select、poll、epoll等函数才应运而生。

3.2.1、select函数

       下面是select函数原型。

       select函数调用过程:

       – 设置文件描述符;

       – 设置监视范围及超时;

       – 调用select函数后查看结果。

       示例代码:

3.3、信 驱动IO

       通过调用sigaction注册信 函数,可以实现信 驱动IO。

       这里解释一下什么是信 驱动IO。

       当我们调用sigaction注册信 函数后,内核会立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信 ,应用进程收到之后在信 处理程序中使用读的操作,可将数据从内核复制到应用进程中。

3.4、异步IO

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

       异步IO和信 驱动IO如何实现这里就暂时不讲了,后面会单独出一篇博客来讲解。

       自己出卖自己:(咳咳,因为很少用到,实际上博主也没深入研究过这东西,目前只掌握理论。)

3.5、异步IO与其他IO

       看到这里,可能会有人疑惑,不是所有IO模型都介绍完了吗里弄个异步IO与其他IO是要做什么/p>

       其实,这里我只是想补充一些大家可能会遗漏,面试中可能会问到的东西。

       那就是,异步IO与其他IO的区别者,信 驱动IO、多路复用IO等算是异步还是同步/p>

       这里,可以告诉大家的是,除了异步IO,其他几种IO模型统统都是同步IO。

3.5.1、同步与异步

       同步IO操作:导致请求进程阻塞,直到IO操作完成。
       异步IO操作:不导致进程阻塞。

       诶,看到这儿,是不是有人就疑惑了,这两种怎么能这么定义区分呢意思是非阻塞IO还是阻塞的不成/p>

       别急,继续往下看。

       可以看到,除了异步IO,其他的IO模型都被阻塞在将数据从内核空间复制到用户空间这一步了。

       这才是同步IO与异步IO真正的区别。

3.5.2、阻塞/非阻塞

       在处理( 络)IO 的时候,阻塞和非阻塞都是同步IO,阻塞与非阻塞的区分在于 络IO时进行IO操作的线程会不会挂起,其实对于某些IO模型来说,阻塞非阻塞其实比较难定义,比如select模型中指用户线程不会阻塞于recvfrom等 络IO操作上,但在select操作本身上是阻塞的,不过因为 络IO并未阻塞的原因仍称selec模型为非阻塞的模型。

文章知识点与官方知识档案匹配,可进一步学习相关知识CS入门技能树Linux入门初识Linux24908 人正在系统学习中

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年9月25日
下一篇 2020年9月25日

相关推荐