操作系统IO原理和五种IO模式

操作系统IO工作原理

  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入设备;

  • 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区。

在这里插入图片描述
读数据:

  • ①首先在网络的网卡上或本地存储设备中准备数据,然后调用read()函数。
  • ②调用read()函数后,由内核将网络/本地数据读取到内核缓冲区中。
  • ③读取完成后向CPU发送一个中断信号,通知CPU对数据进行后续处理。
  • CPU将内核中的数据写入到对应的程序缓冲区或网络Socket接收缓冲区中。
  • ⑤数据全部写入到缓冲区后,应用程序开始对数据开始实际的处理。
  • 程序中试图利用IO机制读写数据时,仅仅只是调用了内核提供的接口函数而已,本质上真正的IO操作还是由内核自己去完成的。

  • Linux 系统为了提高 IO 效率,会在用户空间和内核空间都加入缓冲区(缓冲区可以减少频繁的系统 IO 调 用。系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为 了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区)

五种IO模型

在这里插入图片描述

一个I/O操作分为两个步骤:发起IO请求和实际的IO操作

  • 同步/异步:区别在第二步是否阻塞。阻塞则是同步,os做完IO操作将结果返回则是异步。

  • 阻塞/非阻塞:区别在第一步,发起I/O请求是否会被阻塞。

  • 同步阻塞IO----BIO(Blocking-IO):

    • 当用户程序执行 read ,线程会被一直阻塞,一直等到内核数据准备好,并把数据从内核缓冲区拷贝到应用程序的缓冲区中,当拷贝过程完成,read 才会返回。

    • 阻塞等待的是「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程
      在这里插入图片描述

      BIO这种模型中,为了支持并发请求,通常情况下会采用“请求:线程”1:1的模型。

      原因:每连接每线程的模型,之所以使用多线程,主要原因在于socket.accept()、socket.read()、socket.write()三个主要函数都是同步阻塞的,当一个连接在处理I/O的时候,系统是阻塞的,如果是单线程的话必然就挂死在那里;但CPU是被释放出来的,开启多线程,就可以让CPU去处理更多的事情。

      缺点:

      1. 线程的创建和销毁成本很高,在Linux这样的操作系统中,线程本质上就是一个进程。创建和销毁都是重量级的系统函数。
      2. 线程本身占用较大内存,像Java的线程栈,一般至少分配512K~1M的空间,如果系统中的线程数过千,恐怕整个JVM的内存都会被吃掉一半。
      3. 线程的切换成本是很高的。操作系统发生线程切换的时候,需要保留线程的上下文,然后执行系统调用。如果线程数过高,可能执行线程切换的时间甚至会大于线程执行的时间,这时候带来的表现往往是系统load偏高、CPU sy使用率特别高(超过20%以上),导致系统几乎陷入不可用的状态。
      4. 容易造成锯齿状的系统负载。因为系统负载是用活动线程数或CPU核心数,一旦线程数量高但外部网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使系统负载压力过大。
  • 同步非阻塞IO----NIO(Non-Blocking-IO):

    • 非阻塞的 read 请求在数据未准备好的情况下立即返回,可以继续往下执行,此时应用程序不断轮询内核,直到数据准备好,内核将数据拷贝到应用程序缓冲区,read 调用才可以获取到结果。

    • 这里最后一次 read 调用,获取数据的过程,是一个同步的过程,是需要等待的过程。这里的同步指的是内核态的数据拷贝到用户程序的缓存区这个过程。

      • 应用进程向操作系统内核,发起recvfrom读取数据。
      • 操作系统内核数据没有准备好,立即返回EWOULDBLOCK错误码。
      • 应用程序进程轮询调用,继续向操作系统内核发起recvfrom读取数据。
      • 操作系统内核数据准备好了,从内核缓冲区拷贝到用户空间。
      • 完成调用,返回成功提示。

      无论是阻塞IO还是非阻塞IO,第一阶段都需要调用read来获取数据,区别在于第二阶段:

      • 如果调用read后没有数据,阻塞IO会阻塞进程,非阻塞IO会让应用程序轮询内核。
      • 如果调用read后有数据,则用户进程直接进入第二阶段,读取处理数据。
  • **IO多路复用:**等到内核数据准备好了,主动通知应用进程再去进行系统调用。

    优点:利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
    在这里插入图片描述

    • 系统调用,本意是指调用内核所提供的API接口函数。
    • recvfrom函数则是指经Socket套接 字接收数据,主要用于网络IO操作。
    • read函数则是指从本地读取数据,主要用于本地的文件IO操作。
  • 信号驱动IO:

    • 信号驱动 lO 是与内核建立 SIGIO 的信号关联并设置回调,当内核有 FD 就绪时,会发出 SIGIO 信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。

      缺点:

      • 当有大量IO操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出。
      • 而且内核空间和用户空间的频繁信号交互性能也较低
  • 异步非阻塞IO----AIO(Asynchronous-Non-Blocking-IO):

    • 异步IO的整个过程都是非阻塞的,用户进程调用完异步API后就可以做其他事,内核等待数据就绪并拷贝到用户空间才会递交信号,通知用户进程。

      image-20230213151657791在这里插入图片描述

      AIO模型基于信号驱动实现,异步 IO与信号驱动 IO的主要区别在于:信号驱动 IO 由内核通知何时可以开始一个 IO 操作,而异步 IO由内核通知 IO 操作何时已经完成。

IO多路复用

  • select:

    • 过程:

      • 将已连接的 Socket 都放到一个文件描述符集合fd_set,select 使用固定长度1024的 BitsMap
      • 调用select函数,将fd_set数据拷贝到内核空间
      • 内核遍历fd,判断是否就绪
      • 数据就绪或者超时后,拷贝fd_set数组到用户空间。
      • 在用户态遍历fd_set,找到就绪的fd
    • 问题:

      1. 需要将整个fd_set从用户空间拷贝到内核空间,select结束还要再次拷贝回用户空间

      2. select无法得知具体哪个fd就绪,需要遍历整个fd_set

      3. fd_set大小限制为1024,最多监听1024个fd

  • poll:

    • poll模式和select差不多,只是不用BitsMap来存储fd,而是将数组拷贝至内核转链表存储。
    • 改进:select的fd_set大小固定为1024,poll在内核中采用链表存储,理论上无上限
    • 问题:poll模式理论上链表无上限,但实际上过长遍历性能变差,有上限。

    select和poll:2 次「遍历」文件描述符集合,一次是在内核态里,一个次是在用户态里 ,而且还会发生 2 次「拷贝」文件描述符集合,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。

  • epoll:

    • 过程:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-whMavJYL-1683457068665)(images/5e17bbbc438d4e2680ca71d3ea04b74etplv-k3u1fbpfcp-zoom-in-crop-mark4536000.png)]

      • 在内核中创建epoll实例,eventpoll 函数内部包含了两个东西 :
        • 红黑树 :用来记录所有的 fd
        • 链表 : 记录已就绪的 fd 、
      • 只需要调用一次epoll_ctl函数,将需要监听的fd添加进红黑树中,并设置回调函数,回调函数触发时,会把就绪的fd加入链表中。
      • 然后不断调用epoll_wait函数同步阻塞获取就绪的fd,将就绪的fd就内核空间拷贝到用户空间。
    • 改进:

      1. epoll 在内核里使用红黑树保存要监听的fd,理论上无上限的,增删改查效率高时间复杂度为O(logn)。

      2. 每个fd只需要执行一次epoll_ctl添加到红黑树,以后每次epoll_wait无需传递参数,不会重复拷贝fd到内核空间。

      3. 内核会将就绪的fd直接拷贝到用户空间的指定位置,用户进程无需遍历所有fd就知道哪个fd就绪了。

        select/poll 每次操作时都传入全量的文件描述符集合,而 epoll 因为在内核维护了红黑树,可以保存所有待检测的 socket ,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。

    边缘触发和水平触发:

    水平触发边缘触发最关键的区别就在于当socket中的接收缓冲区还有数据可读时。epoll_wait是否会清空rdllist

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G28Mg4p5-1683457068666)(images/774a0a2b3b8249518040d19947bccf4btplv-k3u1fbpfcp-zoom-in-crop-mark4536000.png)]

黑马程序员Redis入门到实战教程

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Guanam_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值