Skip to content
快速预览

理解IO模型

✍️ w 🕒 2023-07-17 00:13:38(3 months ago) 🔗 B.NodeJS学习 node I/O

I/O(输入/输出)是计算机系统的核心组成部分之一。它是计算机与外部环境进行交互的主要方式,包括与用户的交互(如键盘输入、屏幕显示等),与硬件设备的交互(如读写磁盘、网络通信等),以及程序之间的交互(如文件操作、进程通信等)。甚至输入文字、保存文件、播放音乐等等,都需要涉及到计算机和外部设备之间的交互等等

操作系统是管理和控制计算机硬件和软件资源的程序,它负责管理和调度I/O操作。例如,当一个程序需要读取磁盘上的一个文件时,它会发出一个I/O请求,这个请求会被操作系统接收并处理,操作系统会调度磁盘,将文件的内容读取到内存中,然后通知程序可以进行后续的操作。

这个过程具体来看,将数据从程序的内存中写入到磁盘(外部设备) 文件中,将数据从磁盘(外部设备)文件读取到程序的内存中(外部设备) 文件系统是实现文件IO操作的一种机制,它将磁盘上的数据组织成文件和目录的形式,使得程序可以通过文件名和路径来访问这些数据。操作系统提供了一些API,使得程序可以进行文件IO操作,如文件打开和关闭、文件读取和写入、文件指针操作和文件属性查询等。程序通常使用文件输入流和文件输出流来进行文件读写操作,它们是操作系统提供的高级API,封装了底层的IO操作,使得程序员可以更方便地进行文件读写操作

操作系统提供了一些常用的文件IO操作API(在操作系统中提供的一组用于读取和写入文件的标准函数,这些函数定义了一组通用的接口,可以被多个应用程序和编程语言所使用),以下是其中一些常见的API:

  1. open():打开文件,返回文件句柄。可以指定文件名和打开模式(只读、只写、追加等)。

  2. close():关闭文件。

  3. read():从文件中读取数据。可以指定读取的字节数。

  4. write():向文件中写入数据。可以指定要写入的数据和字节数。

  5. lseek():移动文件指针。可以指定要移动的偏移量和起始位置。

  6. feof():检查文件是否到达文件尾。

  7. ferror():检查文件是否出错。

  8. rewind():将文件指针指向文件开始位置。

  9. fgets():从文件中读取一行数据。可以指定读取的最大字符数和文件句柄。

  10. fputs():将字符串写入文件。可以指定要写入的字符串和文件句柄。

  11. fprintf():将格式化的数据写入文件。可以指定要写入的格式化字符串、参数和文件句柄。

在实际开发中这些方法在不同编程语言中的调用方式和参数可能会有所不同但编程语言提供的操作I/O事件方法具备作用都是类似,正是这样编程语言提供了一些工具和机制,帮助程序捕捉和处理 I/O 事件,使得程序能够更好地处理和响应这些事件,从而提供更好的用户体验或实现特定的功能

综合来说 I/O模型是计算机系统与外部设备之间进行数据交互的过程,包括读取和写入数据。编程语言提供了一些API,帮助程序捕捉和处理I/O事件,使得程序能够更好地处理和响应这些事件,从而提供更好的用户体验或实现特定的功能。I/O 就是指内存与外部设备之间的交互(数据拷贝)我们所说的IO操作不只是磁盘IO,网络IO,也包括内存IO。只要是读写,都算是IO。

I/O和 Cpu 操作一些i/O 设备上

实际关系比我描述要复杂更多不做更深入探讨,有三种控制 I/O 设备的方法

使用程序控制 I/O使用中断驱动 I/O使用 DMA 驱动 I/O

使用程序控制 I/O

  • CPU 请求 I/O 操作,当服务器接收到客户端的请求后,这些请求会被放入一个队列中等待处理。CPU会从队列中取出请求,然后执行相应的计算或处理任务。这个过程中,CPU可能需要调用I/O操作,比如读取硬盘上的数据或者写入数据到硬盘。
  • I/O 模块执行IO操作,当CPU调用I/O操作时,操作系统会将CPU的控制权交给I/O设备(如硬盘),然后CPU会进入等待状态。这种情况下,CPU是被阻塞的,直到I/O操作完成,CPU才能继续执行后续的任务。
  • I/O 模块设置状态位,但不会直接通知 CPU 操作完成也不会中断 CPU
  • CPU 会定期检查状态位
  • CPU 在检查到IO操作完成时进行数据传输

CPU 必须等待很长时间才能等到处理结果。CPU 在等待时会采用轮询(polling)或者 忙等(busy waiting) 的方式,轮询方法通常用于控制低速设备,如键盘和鼠标

图 1

使用中断驱动 I/O

上面的问题是 cpu 不停的去询问整个 I/O 是否操作完成,如果让I/O 可以自行通知,那么就可以解放 cpu

  • CPU 进行读取操作
  • I/O 设备从外围设备获取数据,同时 CPU 执行其他操作
  • I/O 设备中断通知 CPU
  • CPU 请求数据
  • I/O 模块传输数据

但在CPU 和 I/O 模块之前进行大量的逐字传输,因此在大量数据传输中效率仍然很低

中断是一种更高效的方法,它允许设备在就绪时向计算机发送信号,通知计算机可以进行I/O操作。例如,当磁盘已准备好时,磁盘驱动程序将向计算机发送中断信号,告诉计算机可以读取数据了。这样,程序就不必不断地检查设备状态,而是可以等待设备通知

使用 DMA 的 I/O

允许I/O设备直接访问计算机内存,而无需CPU参与,DMA控制器可以向内存中传输数据,从而提高了数据传输速度和CPU的可用性。使用DMA驱动I/O对于需要高速数据传输和减轻CPU负载的应用程序

  • CPU 向 DMA 控制器发出 DMA 请求。
  • DMA 控制器接收到 DMA 请求后,向 I/O 设备发出数据传输请求。
  • I/O 设备将数据传输到内存中的 DMA 缓冲区。
  • DMA 控制器将数据从 DMA 缓冲区传输到内存中的目标地址。
  • DMA 控制器将传输完成的中断信号发送给 CPU。

DMA(直接内存访问):DMA是一种更高效的数据传输方法,它允许设备直接访问计算机内存,而无需CPU参与。使用DMA驱动I/O对于需要高速数据传输和减轻CPU负载的应用程序非常有用,如视频流和音频流处理。或者当从磁盘读取大量数据时,使用DMA可以将数据直接传输到内存中,而无需CPU参与,从而提高了数据传输速度

总结

这三种情是cpu况控制 I/O 设备的方法,从在I/O操作期间,发起I/O请求的进程会被阻塞,CPU需要不停轮询被占用,到由 I/O 发起通知减少cup 的占用,但用说明的是,无论哪一种 cpu 都会在空闲的时刻被释放出来供其他进程或线程使用(轮询时候也会有空闲的cpu处理速度及快)。

编程 时候 I/O 处理方式

根据上面在设备上的处理,可以知道只要,在IO 特定过程的时候其实完全不需要 cpu,将cpu 释放出来即可,让cpu 可以去执行其他任务。

网卡数据流向 CPU 都经过哪些流程 如下图

图 2

用户进程发起一个网络 IO 操作的时候,大致上分为三个部分

  1. 用户进程读取数据,读取数据只会有三个可能 (有数据、没有数据、出错)
  2. 网卡数据从网卡外设到内核空间 (此过程现代计算机是不需要 CPU 参与,网卡控制器通过 DMA 技术直接搬运到内核空间)。数据完成空网卡控制器会发出中断信号。
  3. 数据到内核之后 CPU 负责复制 (在此我们先不考虑零拷贝情况) 到用户空间。

IO操作,依赖底层的IO实现,主要就是read和write两大系统调用。read调用并不是直接从物理设备中将数据读取到用户内存空间中,而是从内核缓冲区复制到进程内存缓冲区。write调用也不是直接将数据写入到物理设备中,而是把进程缓冲区写到内核缓冲区。

网络IO就是等待远端数据陆续到达;磁盘IO就是等到磁盘数据从磁盘读取到内核缓冲区。数据复制。用户空间的程序没有权限直接读取内核缓冲区的数据(操作系统处于安全的考虑),因此内核需要把内核缓冲区的数据复制一份到进程缓冲区。用户进程和内核交互围绕着 1 和 3 进行。到此在 1 和 3 环节就会有不同情况

《UNIX网络编程》书 中把 I/O 进行划分 阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型,前4种为同步IO操作,只有异步IO模型是异步IO操作。

下面要弄清除的就是 同步 异步 阻塞 和 非阻塞 四个概念

  • 同步I/O操作是指应用程序在发起一个I/O操作时,要一直等待I/O操作完成并返回结果,然后才能进行下一步操作。在这个过程中,应用程序会阻塞,直到I/O操作完成并返回结果,才能继续执行下一步操作。阻塞I/O和非阻塞I/O都可以是同步I/O操作,因为它们都需要应用程序等待I/O操作完成并返回结果,简单的说如果将这个阻塞的过程放到另一个线程去执行,那么它就不会占用当前线程的时间了,就应该称它为异步操作。

  • 异步I/O操作是指应用程序在发起一个I/O操作时,不必等待I/O操作完成,就可以继续执行下一步操作。在这个过程中,应用程序不会阻塞,而是会立即返回,并且系统会在后台完成I/O操作。异步I/O操作通常是非阻塞的,因为应用程序不必等待I/O操作完成才能继续往下执行

  • 阻塞是指当进程或线程在执行某个操作时,如果该操作不能立即完成,那么该进程或线程就会被挂起,直到该操作完成后才能继续执行下一步操作。在这个过程中,进程或线程会一直等待该操作完成,并且不能执行其他操作,因此阻塞会导致进程或线程的等待时间增加,降低系统的性能。

  • 非阻塞是指当进程或线程在执行某个操作时,如果该操作不能立即完成,那么该进程或线程会立即返回,并且可以继续执行其他操作。在这个过程中,进程或线程不会一直等待该操作完成,并且可以执行其他操作,因此非阻塞可以提高系统的性能

PS:之前总觉的同步和非阻塞 不能挂成勾,现在结合概念和上面的图来看,只要 我们将实际 不需要 IO 操作的阶段将 cup 释放出去让他可以去操作其他线程或者进程就可以达到 我们说的第一个阶段不阻塞,但是这是短暂只要当IO 在需要cpu 的时候,cpu 还是一样需要回来按照实际运行步骤 解决问题,简单的理解 阻塞和非阻塞 就是 IO 是否阻挡cpu 去做其他出来阶段

同步阻塞I/O

同步阻塞 I/O 是最常见的 I/O 模型之一。在这种模型中,当一个线程发起一个 I/O 请求时,它将被阻塞直到该请求完成并返回结果。在 I/O 操作执行期间,线程无法执行任何其他任务,因此会浪费 CPU 时间。这种模型可以保证 I/O 操作的正确性,但是在等待 I/O 操作完成期间,程序无法执行其他任务,导致程序的性能和并发性受到限制。

举个例子多个客户端连接进来,由于I/O就一个,先进来已经占用了 I/O, 记住读写都在一个I/O 因此读写操作实际也会先后等待执行,此时其他进来的用户必须等待第一个进来的 I/O 操作处理完毕后,才能处理第二个依次类推

就像上图中 1 和 3 两个阶段,用户进程都在休眠状态(进程或线程在进行I/O操作时,如果该操作不能立即完成,就会被挂起,即暂停执行,等待I/O操作完成后再继续执行。)阻塞IO的特点是:在内核进行IO执行的两个阶段,用户线程都被阻塞了

如下图当应用进程在网络IO中发起 recvfrom 系统调用时,内核开始进入准备数据阶段。此时,由于等待IO操作,进程进入阻塞状态。只有当数据准备好且从内核缓冲区拷贝到用户进程中时,进程才能解除阻塞状态,转换为就绪状态,等待被调度运行。因此,在阻塞式IO操作的两个阶段中,用户进程一直处于阻塞状态,除了等待之外不能进行其他操作。

名词解释:recvfrom 是一个系统调用函数,用于接收来自网络的数据。它通常用于在网络编程中接收UDP协议的数据报或TCP协议的数据流。 图 3

解决办法:就是利用多线程并发模式,即一个连接一个线程,缺点在于资源要求太高,系统中创建线程是需要比较高的系统资源的,如果连接数太高,系统无法承受,而且,线程的反复创建-销毁也需要代价,同时,线程的调度、上下文切换乃至它们占用的内存,可能都会成为瓶颈

同步非阻塞IO模型

同步非阻塞IO(None Blocking IO),简称NIO模型(需要注意,这里的NIO和Java里面的NIO不是一个东西!)。 那么recvfrom的系统调用就会返回一个错误信息(EWOULDBLOCK), 有了这个错误信息, 用户进程知道内核还没有把数据准备好, 就可以通过不断轮询的方式一直去问内核:数据准备好了没? 每次都返回这个错误信息, 直到内核里的数据准备好了, 返回OK给用户进程。

用户进程不断轮询的时候, 是完全没有阻塞的, 这也就是称之为非阻塞IO的原因。 然后就可以进入第二个阶段, 内核把数据拷贝到用户进程里, 这个阶段, 进程阻塞了

因此上图中 非阻塞 I/O 此 IO 操作在 1 阶段未被挂起,3 阶段被挂起

问题:不断的轮询对CPU性能造成极大的浪费

图 4

解决办法:就是利用多线程并发模式,即一个连接一个线程,缺点在于资源要求太高,系统中创建线程是需要比较高的系统资源的,如果连接数太高,系统无法承受,而且,线程的反复创建-销毁也需要代价,同时,线程的调度、上下文切换乃至它们占用的内存,可能都会成为瓶颈

同步IO复用模型

同步IO复用模型为了解决,单个进程或者线程能同时处理多个IO,select,epoll,poll是LinuxAPI提供的复用方式。本质上由操作系统内核缓冲IO数据,使得单个进程线程能监视多个文件描述符。

在操作系统中,文件描述符是一种可以用来访问文件或网络套接字的抽象表示。当我们说"监视文件描述符"时,我们实际上是在监视这些文件或网络套接字的状态,这些状态可以分一下。

  1. 可读(Readable) :对应的文件或套接字有数据可读,或者是文件已经到达结尾,或者是套接字已经断开连接(这在某种意义上也可以看作是"有数据可读",因为可以读到一个长度为0的数据,表示对方已经断开连接)。

  2. 可写(Writable) :对应的文件或套接字可以写入数据,如果尝试写入数据不会导致进程阻塞。

  3. 异常(Exceptional) :对应的文件或套接字发生了异常情况。对于套接字来说,这通常表示发生了网络错误,或者是"带外数据"(Out-of-band data)到达。

这些状态可以用来判断文件描述符是否准备好进行IO操作,以及应该进行何种操作。例如,如果一个文件描述符的状态是可读的,那么就可以从中读取数据;如果状态是可写的,那么就可以向其中写入数据;如果状态是异常的,那么就需要进行错误处理。

明白这些基础概念现在只是粗略套路 select,epoll,poll 各自有相对的优缺点,只是讨论整体实现 不套路具体细节

之前 I/O 数据来到直接放到 I/O 中,如果将他们收集一个地方先,这个地方通过监视文件描述符集合中的每个文件描述符,来决定是否能进入 io 按照这个思路

  1. 服务器首先创建一个文件描述符集合,通常包含多个套接字描述符。每个套接字描述符都对应一个客户端连接。

  2. 服务器调用select、poll或epoll等系统调用,需要显式地将对应的文件描述符添加到你想要监视的文件描述符集合中,传入文件描述符集合作为参数。这个系统调用的作用是监视文件描述符集合中的每个文件描述符,看它们是否准备好进行IO操作。这里的"准备好进行IO操作"包括以下几种情况:

    • 对于读操作,有数据可以从文件描述符对应的套接字中读取。
    • 对于写操作,可以向文件描述符对应的套接字中写入数据。
    • 对于异常情况,如连接断开、套接字错误等。
  3. 在系统调用返回之前,服务器进程会被阻塞。这意味着服务器进程在等待系统调用返回期间不会做任何事情,也不会消耗CPU资源。

  4. 当文件描述符集合中的一个或多个文件描述符准备好进行IO操作时,系统调用就会返回。系统调用返回的结果取决于具体使用的是哪个系统调用:

    • 对于select,它会返回一个新的文件描述符集合,这个集合中的文件描述符都已经准备好进行IO操作。
    • 对于poll,它会修改传入的pollfd结构体数组,将已经准备好进行IO操作的文件描述符对应的结构体中的revents字段设置为相应的事件。
    • 对于epoll,它会返回一个包含了已经准备好进行IO操作的文件描述符的epoll_event结构体数组。
  5. 系统调用返回后,服务器就知道哪些文件描述符已经准备好进行IO操作,然后就可以对这些文件描述符进行相应的处理,如读取数据、写入数据或处理异常情况。

这种模型的优点是,你的进程可以同时处理多个IO请求,而不是像传统的阻塞IO模型那样,一次只能处理一个请求,它也是停留在第一阶段优化等内核把数据准备好了,用户线程再发起 read 调用,第二阶段依旧是阻塞。这大大提高了服务器的并发处理能力。

图 5

图 8

信号驱动式I/O(signal-driven I/O)

信号驱动式I/O(Signal-driven I/O)是一种I/O模型,它使用信号(Signal)来通知程序有I/O事件发生,以便程序可以采取相应的操作。

在信号驱动式I/O中,程序首先使用系统调用(如sigaction())来注册I/O事件发生时要接收的信号。然后,程序调用select()或poll()等函数等待I/O事件的发生。当I/O事件发生时,内核会向程序发送信号,程序通过信号处理函数(Signal handler)进行处理。

信号驱动式I/O的优点是它可以同时处理多个文件描述符(File descriptor),而不需要创建线程或进程。此外,信号驱动式I/O还可以避免阻塞,因为程序可以在等待I/O时继续执行其他任务。但是,由于信号处理函数具有异步性质,因此编写和调试信号处理函数可能会比较困难。

这里没具体看

图 6

异步非阻塞 I/O 模型

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

总结图

前四种模型的主要区别都在第一阶段,因为前四种魔性的第二阶段基本相同:在数据从内核拷贝到调用者的缓冲区时,进程阻塞于recvfrom调用。然后,异步I/O模型处理的两个阶段都不用于前四个模型

图 7

参考

彻底弄懂IO复用:IO处理杀手锏,带您深入了解select,poll,epoll原创

五种网络IO模型优缺点

请你说说IO多路复用(select、poll、epoll)

网络IO: 同步、异步、阻塞、非阻塞、IO复用

一文彻底搞定(阻塞/非阻塞/同步/异步)网络IO、并发编程模型、异步编程模型的爱恨情仇

unix网络编程第一卷第六章 I/O

阻塞/非阻塞、同步/异步的概念

linux IO —— 理解阻塞/非阻塞、同步/异步

I/O控制方式(全网最细)

网络通信 - IO模型

什么是 I/O 多路复用

异步IO

Nginx服务之IO模型

说了这么多次 I/O,可你知道其中的原理么

围绕线程

【协程第二话】协程和IO多路复用更配哦~

谈谈你对IO多路复用的理解,全面从select,poll,epoll来进行综合对比,让你offer拿

Released under the MIT License.