关于I/O

1. 概念

  • 主存(Main memory)

    主存储器简称主存,也称内存。是计算机硬件的一个重要部件,其作用是存放指令和数据,并能由中央处理器(CPU)直接随机存取。现代计算机是为了提高性能,又能兼顾合理的造价,往往采用多级存储体系。即由存储容量小,存取速度高的高速缓冲存储器,存储容量和存取速度适中的主存储器是必不可少的。主存储器是按地址存放信息的,存取速度一般与地址无关。32位(比特)的地址最大能表达4GB的存储器地址。这对多数应用已经足够,但对于某些特大运算量的应用和特大型数据库已显得不够,从而对64位结构提出需求。

  • 缓存(Cache)

    每一个现代处理器(CPU)都配置高速缓存(Cache)。目前CPU高速缓存级别主要分为L1/L2/L3三个级别;三个级别的高速缓存,缓存大小逐级增加同时访问速度逐次降低。高速缓存存在的主要原因是解决CPU寄存器与主存在处理速度上不匹配问题,从而极大提高CPU使用效率

  • 页缓存(page cache)

    当应用程序要读取磁盘上的文件的时候,首先需要CPU将磁盘上的文件内容拷贝到主存中,然后我们再从主存中读取文件内容。但是,我们知道从磁盘属于慢速设备,主存属于高速设备,从磁盘传输数据到主存是比较慢的,所以为了均衡这种速度差,就有了类似于CPU 高速缓存一样的东西,叫做页缓存。
    页缓存,利用主存的空闲部分来缓存一些数据块,也就是页缓存中的页面,我们就把这部分缓存磁盘文件的空闲的主存叫做页缓存。

  • 脏数据

    是指事务对缓冲池中行记录的修改,并且还没有提交。

  • 脏读(dirty read)

    脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

  • 脏页

    脏数据和脏页是完全不同的两种概念,脏页指的是在缓冲池中已经被修改的页,但是还没有刷新到磁盘中,即数据库实例内存中的页和磁盘中的页的数据不一致。脏页的读取是非常正常的,脏页是数据库实例内存和磁盘异步造成的,这并不影响数据的一致性,脏页最终会被刷新到磁盘中。

  • 套接字缓冲区(socket buffer)

    操作系统在主存上开辟的一块空间,主要用于缓冲要写入网络的数据。

  • DMA

    直接内存访问(DMA,Direct Memory Access)是一些计算机总线架构提供的功能,它能使数据从附加设备(如磁盘驱动器)直接发送到计算机主板的内存上。
    DMA 技术就是我们在主板上放一块独立的芯片,称为协处理器(DMAC)。在进行内存和 I/O 设备的数据传输的时候,我们不再通过 CPU 来控制数据传输,而直接通过DMA。协处理器没有CPU强大,主要作用就是用于数据传输控制。DMA方式中,主存与I/0设备之间有一条数据通路(专用数据总线),则交换信息时无需像中断一样调用中断服务程序,数据不经过CPU,直接通过这条线传输,所以DMA是需要硬件支持的

1.1 页缓存的简单工作流程

当应用程序读取文件内容的时候, 首先会从页缓存中查找有无数据,如果有直接返回;如果没有,表示缺页,会触发一个缺页中断,然后需要从磁盘上加载,再放入页缓存。

1.2 页缓存的写机制或者写触发的时机

当页缓存页面有脏数据的时候,什么时候这些数据被写入磁盘呢?
第一:当应用程序调用刷盘的系统调用的时候,比如fsync, fdatasync, sync等则会将脏页写回磁盘;
第二:操作系统有一个后台线程,每间隔一定的时间就会将页缓存中的脏数据写入磁盘

注意:
页缓存和主存的关系: 页缓存属于主存的一部分,只不过是主存空闲的部分

1.3 为什么需要套字节缓冲区

我们向网络发送数据,需要对数据添加首部信息,而且还需要把数据拷贝到网卡上,然后由网卡写出。如果写的太频繁,则会需要频繁的发送中断请求和CPU频繁的将数据拷贝到网卡。所以,为了提升写的性能,引出了套接字缓冲区。

1.4 套接字缓冲区的简单流程

套接字缓冲区等待被写满,然后向CPU发出中断请求,执行中断处理程序,通知网卡驱动程序有数据发送,然后由驱动程序从套接字缓冲队列获取读取数据,拷贝到网卡的缓冲队列,然后由网卡写出到网络。

2. 传统I/O方式

2.1 传统I/O读写流程

  • CPU 发出对应的指令给磁盘控制器,然后返回;
  • 磁盘控制器收到指令后,于是就开始准备数据,会把数据放入到磁盘控制器的内部缓冲区中,然后产生一个中断;
  • CPU 收到中断信号后,停下手头的工作,接着把磁盘控制器的缓冲区的数据一次一个字节地读进自己的寄存器,然后再把寄存器里的数据写入到内存,而在数据传输的期间 CPU 是无法执行其他任务的。

在这里插入图片描述

2.2 传统 I/O的性能问题

我们知道,I/O操作分为 磁盘I/O操作网络I/O操作

前者是从磁盘中读取数据源输入到内存中,之后将读取的信息持久化输出在物理磁盘上;后者是从网络中读取信息输入到内存,最终将信息输出到网络中。

不管是磁盘I/O还是网络I/O,都是以字节为单位,在高并发、大数据场景中,很容易导致阻塞,因此性能是非常差的。还有,输出数据从用户空间复制到内核空间,再复制到输出设备,这样的操作会增加系统的性能开销,接下来我们来具体分析一下:

  1. 多次内存复制

输入操作在操作系统中的流程如下图所示:
在这里插入图片描述
User Space:指操作系统中的用户态,Kernel Space 是指操作系统中的 内核态,定义如下:

  • 用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
  • 内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。

流程说明:

步骤1:用户态发出 read() 系统调用,向内核发起读请求;

步骤2:内核向硬件发送读指令,并等待读就绪;

步骤3:通过DMA,内核把将要读取的数据复制到指向的内核缓存中;

步骤4:操作系统内核将数据复制到用户空间缓冲区,然后read系统调用返回。

在这个过程中,数据先从外部设备复制到内核空间,再从内核空间复制到用户空间,这就发生了两次内存复制操作。这种操作会导致不必要的数据拷贝和上下文切换,从而降低I/O的性能。

  1. 阻塞

在传统 I/O 中,InputStream 的 read() 是一个 while 循环操作,它会一直等待数据读取,直到数据就绪才会返回。这就意味着如果没有数据就绪,这个读取操作将会一直被挂起,用户线程将会处于阻塞状态。

在少量连接请求的情况下,使用这种方式没有问题,响应速度也很高。但在发生大量连接请求时,就需要创建大量监听线程,这时如果线程没有数据就绪就会被挂起,然后进入阻塞状态。一旦发生线程阻塞,这些线程将会不断地抢夺CPU资源,从而导致大量的CPU上下文切换,增加系统的性能开销。

3. DMA技术

将IO设备与内存之间的数据传输工作交给DMA控制器,这样 CPU 就可以去处理别的事务

3.1 将数据写入磁盘的流程

  • 第一:用户程序将数据写入用户空间的缓冲区,然后调用write系统调用
  • 第二:CPU进入内核态,将数据写入拷贝到页缓存,写入成功,立即返回
  • 第三:用户进程调用系统调用fsync、fdatasync、sync等将页缓存中的脏页刷到磁盘;如果用户没有调用,操作系统后台线程也会间隔一段时间就刷盘。
  • 第四:CPU执行刷盘指令,会向磁盘控制器(DMA模式)发送写指令,告诉磁盘控制器要刷盘的数据在主存的位置、要刷盘多少数据、要刷到磁盘什么位置等,然后返回干其他事情了
  • 第五:DMA引擎向CPU发送DMA请求,申请控制总线和主存,申请成功后,开始从主存页缓存中将数据写入到磁盘。写完了之后,字计数器溢出,中断机构会向CPU发出中断请求,CPU收到中断请求后,开始处理中断程序,进行一些扫尾的工作,然后结束。

3.2 从磁盘读取数据的流程

  • 第一:用户程序调用read系统调用
  • 第二:CPU进入内核态,操作系统会检查页缓存是否有数据。如果有CPU拷贝这些数据到用户空间;如果没有则触发缺页中断,需要向磁盘调页,即从磁盘加载数据到页缓存
  • 第三:CPU向磁盘控制器发送读指令,并且告诉磁盘主存地址、要读取的数据长度、要从哪一个设备读,然后返回干其他事情了
  • 第四:磁盘控制器开始准备数据,磁盘数据准备好则放入数据缓冲区,并且通知DMA引擎。
  • 第五:DMA引擎向CPU发送DMA请求,申请获取总线和主存的使用权,申请成功后,开始将缓冲区寄存器的数据拷贝到主存
  • 第六:完成数据传输后,磁盘控制器中的中断机构向CPU发送中断信号,触发中断处理程序,进行扫尾工作
  • 第七:页缓存把数据返回给用户进程(取决于I/O模式,如果是同步读,则需要线程同步等待数据准备好)

在这里插入图片描述

4. 网络数据传输流程

4.1 将数据写入网络的流程

  • 第一:用户程序调用套接字API,比如write或者send系统调用
  • 第二:CPU进入内核态,此时会将数据拷贝到套接字缓冲区
  • 第三:协议栈处理套接字缓冲区的数据,但并不是立即发送。不立即发送的原因就是,每次写的数据大小是由应用程序决定,如果每次要发送的数据太少,就会发送大量的小数据包,导致网路效率下降,所以需要积累到一定数量再发送出去。一般来说是根据MTU来决定,以太网中MTU一般是1500字节,如果(数据包大小+40(数据帧的首部长度)) > MTU,则会分片,然后满了再发送出去;到那时如果没有满,难道一直等到数据包满了才可以发送吗,协议栈内部有一个计时器,到期了也会将数据发送出去
  • 第四:需要发送数据包的时候,协议栈会产生一个软中断,触发CPU中断处理程序,告诉网卡驱动程序有新的网络包需要发送
  • 第五:网卡驱动程序会从套接字缓冲区读取要发送的数据包,通过DMA拷贝到网络接口控制器(网卡)的数据缓冲区
  • 第六:等待网卡的数据缓冲区写满了之后,就会把数据发送出去

4.2 从网络读取数据的流程

  • 第一: 网卡收到网络数据包,放入数据缓冲区
  • 第二: 数据缓冲区满了,则通知DMA引擎
  • 第三: DMA引擎向CPU申请总线和主存的使用权,将缓冲区数据拷贝到主存的套接字缓冲区
  • 第四: 当拷贝完成后,操作系统怎么知道有数据包到来?所以需要一种机制通知操作系统。最简单方式就是触发中断,但是如果数据量很大,可能就会频繁触发中断,导致CPU没有时间执行其他的程序,从而影响系统性能。Linux 2.6引入了New API机制,综合中断和轮询的方式来接收网络数据包:第一次通过中断触发中断服务处理程序,然后唤醒软中断采用轮询方式来轮询数据,直到没有数据时才恢复中断,这样一次中断可以处理多个数据包,降低网卡中断带来的性能消耗。
  • 第五:网络控制器(网卡)的中断机构向CPU发出中断请求,CPU会执行内核中网卡驱动程序的处理逻辑:
    • #1 禁用网卡中断:避免后续数据处理频繁的向CPU发起中断
    • #2 打开软中断:有可能后续的中断服务处理程序执行时间长,会影响CPU执行其他的进程或者中断请求,所以使用软中断,主要通过轮询的方式获取数据并处理数据
  • 第六:软中断交给协议栈进行处理,协议栈对数据包进行拆包,并且将等待队列中的处于等待的线程加入到就绪队列,等待被CPU再次调度
    • #1 链路层识别上册协议(IPV4 or IPV6),去掉帧头和帧尾,然后交给网络层
    • #2 网络层取出IP头,判断网络包目标主机,如果不是当前主机,则转发;如果是去掉IP头,然后交给传输层
    • #3 取出TCP或者UDP头后,根据<源IP、源端口、目标IP、目标端口>四元组作为标识,找到对应的socket
      在这里插入图片描述

所以,制约网络拷贝数据速率的因素,包括:

  • 网络
  • 网卡
  • 硬盘(本身读写速度和硬盘缓存)
  • 内存速率
  • CPU(传统I/O)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值