5种IO模型

一、概述

五种IO模型包括:阻塞IO、非阻塞IO、信号驱动IO、IO多路转接、异步IO。其中,前四个称为同步IO。

1、什么是IO

IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:用户进程空间<–>内核空间、内核空间<–>设备空间(磁盘、网络等)。IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。网络I/O就是通过网络进行数据的拉取和输出。磁盘I/O主要是对磁盘进行读写工作。

比如你打开浏览器,访问腾讯首页,浏览器这个程序就需要通过网络IO获取腾讯的网页。浏览器首先会发送数据给腾讯服务器,告诉它我想要首页的HTML,这个动作是往外发数据,叫Output,腾讯服务器把网页发过来,这个动作是从外面接收数据,叫Input。所以,通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。

流是什么?可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和腾讯服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。

在这里插入图片描述
LINUX中进程无法直接操作I/O设备,其必须通过系统调用请求kernel来协助完成I/O动作;内核会为每个I/O设备维护一个缓冲区。
对于一个输入操作来说,进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备中读取,因为设备IO一般速度较慢,需要等待;内核缓冲区有数据则直接复制到进程空间。
所以,对于一个网络输入操作通常包括两个不同阶段:

*等待网络数据到达网卡→读取到内核缓冲区,数据准备好;
*从内核缓冲区复制数据到进程空间

2、什么是用户空间和内核空间

虚拟内存被操作系统划分成两块:内核空间和用户空间。
为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

内核空间是内核代码运行的地方,
用户空间是用户程序代码运行的地方。

当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态。

3、什么是同步和异步

由于CPU和内存的速度远远高于外设的速度。所以在IO编程中,就存在速度严重不匹配的问题。

举个例子:
我们都是通过热水壶来烧水的。在很久之前,科技还没有这么发达的时候,如果我们要烧水,需要把水壶放到火炉上,我们通过观察水壶内的水的沸腾程度来判断水有没有烧开。
随着科技的发展,现在市面上的水壶都有了提醒功能,当我们把水壶插电之后,水壶水烧开之后会通过声音提醒我们水开了。
传统水壶的烧水就是同步的,电热水壶的烧水就是异步的。

同步请求:A调用B,B的处理是同步的,在处理完之前他不会通知A,只有处理完之后才会明确的通知A。
异步请求:A调用B,B的处理是异步的,B在接到请求后先告诉A我已经接到请求了,然后异步去处理,处理完之后通过回调等方式再通知A。

同步和异步最大的区别就是被调用方的执行方式和返回时机。同步指的是被调用方做完事情之后再返回,异步指的是被调用方先返回,然后再做事情,做完之后再想办法通知调用方。

4、什么是阻塞和非阻塞

还是那个烧水的例子,当你把水放到水壶里面,按下开关后,你可以坐在水壶前面,别的事情什么都不做,一直等着水烧好。你还可以先去客厅看电视,等着水开就好了。
对于你来说,坐在水壶前面等就是阻塞的,去客厅看电视等着水开就是非阻塞的。

阻塞请求,A调用B,A一直等着B的返回,别的事情什么也不干。
非阻塞请求,A调用B,A不用一直等着B的返回,先去忙别的事情了。

所以说,阻塞和非阻塞最大的区别就是在被调用方返回结果之前的这段时间内,调用方是否一直等待。阻塞指的是调用方一直等待,别的事情什么都不做。非阻塞指的是调用方先去忙别的事情。
特点

  • 只有处于运行状态的进程(获得CPU)才能被阻塞
  • 阻塞是主动行为
  • 不占用CPU资源

5、进程切换

1)定义

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。即从用户态(较低的3G字节)切换到内核态(最高的1G字节),非常消耗系统资源。

(2)过程

  • 保存处理机上下文,包括程序计数器和其他寄存器。
  • 更新PCB信息。
  • 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
  • 选择另一个进程执行,并更新其PCB。
  • 更新内存管理的数据结构。
  • 恢复处理机上下文。

二、五种IO详解

2.1 阻塞IO模型

当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。
在这里插入图片描述
进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程。操作成功则进程获取到数据
在这里插入图片描述
实例
Blocking I/O是在I/O执行的两个阶段都被block了。
例如:我要去饭堂吃饭,这时饭堂的人很多,我就得排队买饭,排队的时间被浪费了
特点

  • 能够及时返回数据,无延迟
  • 性能下降

2.2 非阻塞IO模型

进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回给进程。

对于上面的阻塞IO模型来说,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。
在这里插入图片描述
在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

例如:

  • nonblocking I/O是在I/O执行的第二个阶段(数据复制)被block了,而第一个阶段并未阻塞(数据准备)。

  • 例如:我要去饭堂吃饭,这时饭堂的人很多,一般来说我需要排队买饭,但我们饭堂的管理最近变的比较人性化,你点完饭后,会给你一个号码,但饭堂噪声很大,我不得不频繁的询问我的饭是否做好了,但是我可以利用之前排队的时间去买瓶饮料喝!
    特点:

  • 拷贝数据的整个过程,进程仍然是阻塞的

  • 需要不断询问数据是否准备好了

  • 能够在等待任务完成的过程中处理其他事件

  • 由于需要轮询,所以延迟会增加

2.3 IO复用模型

多个的进程的IO可以注册到一个复用器(select)上,然后用一个进程调用该select,,select会监听所有注册进来的IO。

如果select没有监听的IO在内核缓冲区都没有可读数据,select调用进程会被阻塞;而当任一IO在内核缓冲区中有可数据时,select调用就会返回;而后select调用进程可以自己或通知另外的进程(注册进程)来再次发起读取IO,读取内核中准备好的数据。
在这里插入图片描述
分析:

  • 从上图可以看到在I/O复用模型中,由于同步非阻塞方式需要不断主动轮询,轮询占据了很大一部分过程,轮询会消耗大量的CPU时间,而 “后台” 可能有多个任务在同时进行,

  • 如果循环查询多个任务的完成状态,只要有任何一个任务完成,就去处理它。轮询不是进程的用户态。这时 “IO 多路复用”就出现了。即UNIX/Linux 的 select、poll、epoll

  • IO多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。

  • 从整个IO过程来看,他们都是顺序执行的,因此可以归为同步模型(synchronous)。都是进程主动等待且向内核检查状态

(2)实例

多路复用I/O执行的两个阶段用户进程都是阻塞的,但是两个阶段是独立的。

例如:我要去饭堂吃饭,这时饭堂的人很多,点完饭后,我会拿到一个号码,以前我不得不频繁的询问我的饭做好了没,但是最近饭堂安装了一块电子显示屏,你的饭好了就会在屏上显示出来,这时候我就不用频繁的去问了,直接看电子显示屏就醒了,然后我就可以利用这个时间去超市买个牙膏了。

(3)特点

-select/poll调用后会阻塞进程,但可以同时阻塞多个IO事件操作(文件描述符),有数据可读或可写(就绪事件),就通知用户进程。
-select 需要每次注册事件(轮询),而epoll不需要每次注册事件(没有轮询,回调函数)

IO多路复用阻塞在select/epoll的系统调用之上的,而真正的IO系统调用如recvfrom是非阻塞的。
(4)适用场景
服务器需要同时处理多个处于监听状态或连接状态的套接字
服务器需要处理多种网络协议的套接字

2.4信号驱动IO模型

在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中,对TCP套接口几乎是没用的,原因是该信号产生得过于频繁,并且该信号的出现并没有告诉我们发生了什么事情。
在这里插入图片描述
当进程发起一个IO操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据

**分析:**从上图可以看出,只有在I/O执行的第二阶段阻塞了用户进程,而在第一阶段是没有阻塞的。该模型在I/O执行的第一阶段,当数据准备完成之后,会主动的通知用户进程数据已经准备完成,即对用户进程做一个回调。该通知分为两种,一为水平触发,即如果用户进程不响应则会一直发送通知,二为边缘触发,即只通知一次。
实例

  • 信号驱动I/O执行的第一阶段不阻塞,而第二阶段是阻塞的。
  • 例如:我要去饭堂吃饭,这时饭堂的人很多,点完饭后,我会拿到一个号码,虽然说饭堂安装了一块电子显示屏,但我在玩手机时还不得不抬头看一下显示屏上有我的号码没,最近饭堂买了一个大喇叭,哪个号码好了,卖饭的阿姨就会用喊,虽说饭堂有点吵,但这个声音还是可以听到的,这样我就可以专心的低头玩手机了。

2.5 异步IO模型

当进程发起一个IO操作,进程返回(不阻塞),但也不能返回结果;内核把整个IO处理完后,会通知进程结果。如果IO操作成功则进程直接获取到数据

在这里插入图片描述
分析:从上图可以看出,在该模型中,当用户进程发起系统调用后,立刻就可以开始去做其它的事情,然后直到I/O执行的两个阶段都完成之后,内核会给用户进程发送通知,告诉用户进程操作已经完成了。
实例

  • 异步 I/O执行的两个阶段都不会阻塞。
  • 例如:我要去饭堂吃饭,估计这会饭堂的人很多,但最近我们饭堂可以叫外卖了,这样就省事多了,我直接打个电话,订份饭送到我们宿舍,而我现在就可以利用原来去饭堂路上和等饭的时间写博客了。
  • 这就是同步和异步的区别,原来我得亲自去饭堂买饭,而现在我可以在宿舍叫外卖。
    特点:
    读写操作由内核完成,完成后内核将数据放到指定的缓冲区,通知应用程序来取。

注意:
此模型和前面模型最大的区别是:前4个都是阻塞的,因为需要自己把用户准备好的数据,放在我的用户空间,而全异步都帮我们做好了。
用户线程完全不需要关心实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。它是最理想的模型。

三、五种IO模型的区别

nginx相比于apache有更高的并发,可以接收处理更多的访问请求,这得益于IO模型

同步I/O引起进程阻塞,直到I/O操作完成
异步I/O不会引起进程阻塞

阻塞式I/O, 非阻塞I/O, I/O复用由于都导致了请求进程阻塞,所以均属于同步I/O。
在这里插入图片描述
nginx高并发使用的是epoll的方式,提供给用户访问,复制数据的一些操作交由内核完成。自身做的事情越少接待的用户请求就越多。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值