IO模式都答不好,怎么通过大厂面试?

希望大家能关注点赞,您的小小举动却能给予我鼓励,会激励我继续创作出高质量文章😘,更多内容可看我主页~

目录

    • 1、IO相关定义
    • 2、UNIX系统下常用的IO模式
        • IO模式主要分为五种类型: 同步阻塞IO、同步非阻塞IO、IO多路复用、信号驱动IO、异步IO
      • 1、同步阻塞I/O(BIO)
      • 2、 同步非阻塞I/O(NIO,Non-Blocking IO)
      • 3、I/O多路复用
        • 1、什么是多路复用
      • 4、信号驱动I/O(SIGIO)
      • 5、异步I/O
      • 6、IO模型比较
        • 1、同步与异步的定义
        • 2、阻塞与非阻塞的定义
        • 3、**阻塞IO调用和非阻塞IO调用、阻塞IO模型和非阻塞IO模型**

1、IO相关定义

​ 为了确保操作系统的安全稳定运行,操作系统启动后,将会开启保护模式:将内存分为内核空间(内核对应进程所在内存空间)和用户空间,进行内存隔离。我们构建的程序将运行在用户空间,用户空间无法操作内核空间,也就意味着用户空间的程序不能直接访问由内核管理的I/O,比如:硬盘、网卡等。

​ 但操作系统向外提供API,其由各种类型的系统调用(System Call)组成,以提供安全的访问控制。
​ 所以应用程序要想访问内核管理的I/O,必须通过调用内核提供的系统调用(system call)进行间接访问

所以I/O之于应用程序来说,强调的通过向内核发起系统调用完成对I/O的间接访问。换句话说应用程序发起的一次IO操作实际包含两个阶段:

  1. IO调用阶段:应用程序进程向内核发起系统调用
  2. IO执行阶段:内核执行IO操作并返回
    2.1. 准备数据阶段: 内核等待I/O设备准备好数据
    2.2. 拷贝数据阶段: 将数据从内核缓冲区拷贝到用户空间缓冲区

阻塞I/O与非阻塞I/O之间的区别在于:在应用程序进程发起IO调用后,到IO执行阶段返回之前,应用程序进程/线程 所处的状态 阻塞与非阻塞 成为这两种IO模式的最大区分。

怎么理解准备数据阶段呢?
对于写请求:等待系统调用的完整请求数据,并写入内核缓冲区;
对于读请求:等待系统调用的完整请求数据;(若请求数据不存在于内核缓冲区)则将外围设备的数据读入到内核缓冲区。

在这里插入图片描述

2、UNIX系统下常用的IO模式

IO模式主要分为五种类型: 同步阻塞IO、同步非阻塞IO、IO多路复用、信号驱动IO、异步IO

1、同步阻塞I/O(BIO)

应用程序中进程在发起IO调用后至内核执行IO操作返回结果之前,若发起系统调用的线程一直处于等待状态,则此次IO操作为阻塞IO。阻塞IO简称BIO,Blocking IO
在这里插入图片描述

BIO带来了一个问题:如果内核数据需要耗时很久才能准备好,那么用户进程将被阻塞,浪费性能。为了提升应用的性能,虽然可以通过多线程来提升性能,但线程的创建依然会借助系统调用,同时多线程会导致频繁的线程上下文的切换,同样会影响性能。所以要想解决BIO带来的问题,我们就得看到问题的本质,那就是阻塞二字

典型应用:阻塞socket、Java BIO;

特点:

  • 进程阻塞挂起不消耗CPU资源,及时响应每个操作;
  • 实现难度低、开发应用较容易;
  • 适用并发量小的网络应用开发;

2、 同步非阻塞I/O(NIO,Non-Blocking IO)

应用程序中进程在发起IO调用后至内核执行IO操作返回结果之前,若发起系统调用的线程不会等待而是立即返回,则此次IO操作为非阻塞IO模型。非阻塞IO简称NIO,Non-Blocking IO。

在这里插入图片描述

​ 非阻塞IO虽然相对于阻塞IO大幅提升了性能,但依旧不是完美的解决方案,其依然存在性能问题,也就是频繁的轮询导致频繁的系统调用,会耗费大量的CPU资源。比如当并发很高时,假设有1000个并发,那么单位时间循环内将会有1000次系统调用去轮询执行结果,而实际上可能只有2个请求结果执行完毕,这就会有998次无效的系统调用,造成严重的性能浪费。有问题就要解决,那NIO问题的本质就是频繁轮询导致的无效系统调用

典型应用: socket是非阻塞的方式(设置为NONBLOCK)

特点:

  • 进程轮询(重复)调用,消耗CPU的资源;
  • 实现难度低、开发应用相对阻塞IO模式较难;
  • 适用并发量较小、且不需要及时响应的网络应用开发;

3、I/O多路复用

1、什么是多路复用
  • IO 多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;

  • 一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;

  • 没有文件句柄就绪就会阻塞应用程序,交出CPU。

    多路是指网络连接,复用指的是同一个线程。

I/O多路复用模型中,线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。read 调用的过程 (数据从内核空间->用户空间)还是阻塞的。(简单的说就是多路复用模型只需要发送一个select调用,不需要像NIO模型那样不断发起调用查看数据是否准备好,节省了应用程序发起的系统调用频次,但内核的工作量只增不减)

在这里插入图片描述
在这里插入图片描述

Select是内核提供的系统调用,它支持一次查询多个系统调用的可用状态,当任意一个结果状态可用时就会返回,用户进程再发起一次系统调用进行数据读取。

select/epoll 虽然解决了NIO重复无效系统调用用的问题,但同时又引入了新的问题。问题是:

  1. 用户空间和内核空间之间,大量的数据拷贝
  2. 内核循环遍历IO状态,浪费CPU时间

换句话说,select/poll虽然减少了用户进程的发起的系统调用,但内核的工作量只增不减。在高并发的情况下,内核的性能问题依旧。所以select/poll的问题本质是:内核存在无效的循环遍历

**针对select/pool引入的问题,**我们把解决问题的思路转回到内核上,如何减少内核重复无效的循环遍历呢?变主动为被动,基于事件驱动来实现。其流程图如下所示:

在这里插入图片描述

epoll相较于select/poll,多了两次系统调用,其中epoll_create建立与内核的连接,epoll_ctl注册事件,epoll_wait阻塞用户进程,等待IO事件,以下是这三种系统调用方法的对比。

在这里插入图片描述

epoll,已经大大优化了IO的执行效率,但在IO执行的第一阶段:数据准备阶段都还是被阻塞的。 所以这是一个可以继续优化的点。

典型应用: select、poll、epoll三种方案,nginx都可以选择使用这三个方案;Java NIO;

特点:

  • 专一进程解决多个进程IO的阻塞问题,性能好;Reactor模式;
  • 实现、开发应用难度较大;
  • 适用高并发服务应用开发:一个进程(线程)响应多个请求;

4、信号驱动I/O(SIGIO)

在这里插入图片描述

当进程发起一个IO操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。

特点: 回调机制,实现、开发应用难度大;

尽管信号驱动模型在内核数据准备阶段不发生阻塞,但在数据拷贝阶段还是发生阻塞。

这时候还存在的问题就是:IO执行的第二阶段还是阻塞,如何变成非阻塞。

5、异步I/O

异步IO真正实现了IO全流程的非阻塞。用户进程发出系统调用后立即返回,内核等待数据准备完成,然后将数据拷贝到用户进程缓冲区,然后发送信号告诉用户进程IO操作执行完毕(与SIGIO相比,一个是发送信号告诉用户进程数据准备完毕,一个是IO执行完毕)。
在这里插入图片描述

特点:

  • 不阻塞,数据一步到位;Proactor模式;
  • 需要操作系统的底层支持,LINUX 2.5 版本内核首现,2.6 版本产品的内核标准特性;
  • 实现、开发应用难度大;
  • 非常适合高性能高并发应用;

**典型应用:**JAVA7 AIO、高性能服务器应用

6、IO模型比较

在这里插入图片描述
在这里插入图片描述

1、同步与异步的定义
  • 同步:发起一个fn的调用,需要等待调用结果返回,该调用结果要么是期望的结果要么是异常抛出的结果,可以说是原子性操作(要么成功要么失败返回)
  • 异步: 发起一个fn调用,无需等待结果就直接返回,只有当被调用者执行处理程序之后通过“唤醒”手段通知调用方获取结果(唤醒的方式有回调,事件通知等)
  • 小结: 同步和异步关注的是程序之间的通信
2、阻塞与非阻塞的定义
  • 阻塞: 类比线程阻塞来说明,在并发多线程争抢资源的竞态条件下,如果有一个线程已持有锁,那么当前线程将无法获取锁而被挂起,处于等待状态
  • 非阻塞: 一旦线程释放锁,其他线程将会进入就绪状态,具备争抢锁的资格
  • 小结: 阻塞与非阻塞更关注是程序等待结果的状态
  • 由此可知,同步异步与阻塞非阻塞之间不存在关联,关注的目标是不一样的
3、阻塞IO调用和非阻塞IO调用、阻塞IO模型和非阻塞IO模型

注意这里的阻塞IO调用和非阻塞IO调用不是指阻塞IO模型和非阻塞IO模型:

  • 阻塞IO调用 : 在用户进程(线程)中调用执行的时候,进程会等待该IO操作,而使得其他操作无法执行。
  • 非阻塞IO调用: 在用户进程中调用执行的时候,无论成功与否,该IO操作会立即返回,之后进程可以进行其他操作(当然如果是读取到数据,一般就接着进行数据处理)。

这个直接理解就好,进程(线程)IO调用会不会阻塞进程自己。所以这里两个概念是相对调用进程本身状态来讲的。

从上面对比图片来说,阻塞IO模型是一个阻塞IO调用,而非阻塞IO模型是多个非阻塞IO调用+一个阻塞IO调用,因为多个IO检查会立即返回错误,不会阻塞进程。

而上面也说过了,非阻塞IO模型对于阻塞IO模型来说区别就是,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暮起

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值