零拷贝技术原理与使用

零拷贝技术的使用

概念

零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。

目标

在应用程序与操作系统中使用零拷贝技术并非是没有拷贝,而是减少CPU拷贝的次数,达到提升应用程序的执行效率。
在了解零拷贝技术以及概念之前,需先了解一下CPU上下文切换DMA以及Buffer/Cache

CPU上下文切换

简介

上下文切换(context-switch)指的是内核(操作系统的核心)在CPU上对进程或者线程进行切换。上下文切换过程中的信息被保存在进程控制块(PCB-Process Control Block)中。PCB又被称作切换帧(SwitchFrame)。上下文切换的信息会一直被保存在CPU的内存中,直到被再次使用。
在系统中上下文切换总共分为如下:

  • 进程上下文切换
  • 线程上下文切换
  • 中断上下文切换

进程上下文切换

进程内部上下文切换

出于安全考虑,将CPU的运行等级分为4种:Ring0、Ring1、Ring2、Ring3,运行等级依次降低,操作系统内部指令只会在Ring0级别的CPU上运行,而第三方任意应用程序均是在Ring3级别上运行。在Linux和Windows系统上只采用了Ring0以及Ring3级别,也称为内核空间(Ring0)以及用户空间(Ring3)。

运行等级如下:
请添加图片描述
当CPU运行级别从内核态切换成用户态或者从用户态切换成内核态时,发生在进程内部的上下文切换;CPU从用户态切换到内核态时,需要做的操作如下:

  • CPU寄存器会将当前进程用户态所在的指令位置保存起来。
  • 切换CPU运行等级
  • 更新进程内核态指令位置
  • 执行内核态任务

这就是一次CPU进程上下文切换,如果从内核态在切换到用户态时,需要再做一次上面的操作。
在进程内部中用户态与内核态之间的切换,也叫特权模式切换;对于发起这种用户态与内核态之间的切换操作称之为系统调用;比如程序需要readwriteopen以及close等对硬件(包括磁盘或者网卡)发起的操作都属于系统调用
对于CPU的运行等级,则是保存在CPU的段寄存器当中,确切的说是段寄存器中的cs(Code Segment),进行用户态与内核态之间的切换,则是更改cs段寄存器中的RPL的运行等级标识位。

CPU寄存器大致结构如下:
请添加图片描述

进程之间上下文切换

进程由内核管理和调度,进程的切换只能发生在内核态,进程上下文不仅包括进程的虚拟内存、栈、全局变量等用户空间资源,还包括内核堆栈、寄存器等内核空间状态。每次进程上下文切换需要几十纳秒到数微秒的CPU时间。
不同进程之间发生上下文切换过程:

  • 首先将进程的虚拟内存、堆栈、全局变量信息等等保存,在保存进程内核态和CPU寄存器信息
  • 加载下一个进程的虚拟内存、堆栈、全局变量信息、内核态信息
  • 刷新进程的虚拟内存和用户栈信息
  • 开始执行下一个进程

并且Linux通过TLB来管理虚拟内存到物理内存之间的映射,当虚拟内存更新后,TLB也需要刷新,内存的访问也会随之变慢。特别在多处理器系统上,缓存被多个处理器共享,刷新缓存不仅会影响当前处理器的进程,还会影响共享缓存的其他处理器的进程。

发起进程上下文切换时机

在进程调度的时候,需要进行切换上下文。Linux为每个CPU维护一个就绪队列,将活跃进程(正在运行和正在等待CPU的进程)按照优先级和等待CPU的时间来排序,然后选择最需要CPU的进程运行。(优先级高和等待时间长的进程)
发生了以下五点是会出现进程上下文切换:

  1. 为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行。
  2. 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行。
  3. 当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度。
  4. 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行。
  5. 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。

线程上下文切换

线程是处理器任务调度和执行的基本单位,进程是操作系统资源分配的基本单位。内核中的任务调度,实际上的调度对象是线程,而进程只是给线程提供了虚拟内存、全局变量等资源。所以,对于线程和进程,我们可以这么理解:

  • 当进程只有一个线程时,可以认为进程就等于线程。
  • 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。
  • 另外,线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的.

那么线程的上下分可以分为

  1. 进程内线程的上下文切换
  2. 进程间线程的上下文切换

理解为:进程内的线程上下文切换吗,会有共享的资源不需要保存和重新加载,比进程间的线程上下文切换消耗更少的资源。合理的在进程内使用多线程可以提高效率和负载。

中断上下文切换

操作系统将中断分为硬中断以及软中断

硬中断

由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。因此是由外部事件引起的因此具有随机性和突发性。

软中断

为了满足实时系统的要求,中断处理应该是越快越好。Linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。
例如:系统调用就是通过软中断进行实现的。

DMA

简介

直接内存访问(DMA,Direct Memory Access)是一些计算机总线架构提供的功能,它能使数据从附加设备(如磁盘驱动器)直接发送到计算机主板的内存上。
允许不同速度的硬件设备来沟通,而不需要依于中央处理器的大量中断负载。否则,中央处理器需要从来源把每一片段的数据复制到寄存器,然后把它们再次写回到新的地方。在这个时间中,中央处理器对于其他的工作来说就无法使用。

DMAC

DMA控制器,本质是上位于主板上的一个独立的芯片,主要工作是用于协助CPU完成一些数据传输工作,称之为协处理器,即Co-Processor。
使用DMA进行传输数据的过程如下:

  1. 首先,CPU 还是作为一个主设备,向 DMAC 设备发起请求。这个请求,其实就是在DMAC 里面修改配置寄存器。
  2. CPU修改DMAC的配置的时候,会告诉DMAC这样几个信息
    • 首先是源地址的初始值和传输时候的地址增减方式
      • 所谓源地址,就是数据要从哪里传输过来。如果我们要从内存里面写入数据到硬盘上,那么就是要读取的数据在内存里面的地址。如果是从硬盘读取数据到内存,那么就是硬盘的IO接口的地址
      • IO的地址可以是一个内存地址,也可以是一个端口地址。而地址的增减就是说,数据时从大的地址向小的地址传输,还是从小的地址向大的地址传输
    • 其次就是目标地址(数据传输的目的地)初始值和传输时候的地址增减方式
    • 第三个自然是要传输的数据长度,也就是我们一共要传输多少数据
  3. 设置完这些信息之后,DMAC就会变成一个空闲的状态(Idle)
  4. 如果我们要从硬盘上往内存里面加载数据,这个时候,硬盘就会向 DMAC 发起一个数据传输请求。这个请求并不是通过总线,而是通过一个额外的连线
  5. 然后,我们的 DMAC 需要再通过一个额外的连线响应这个申请。
  6. 于是,DMAC 这个芯片,就向硬盘的接口发起要总线读的传输请求。数据就从硬盘里面,读到了 DMAC 的控制器里面。
  7. 然后,DMAC 再向我们的内存发起总线写的数据传输请求,把数据写入到内存里面。
  8. DMAC 会反复进行上面第 6、7 步的操作,直到 DMAC 的寄存器里面设置的数据长度传输完成。
  9. 数据传输完成之后,DMAC 重新回到第 3 步的空闲状态。

总结

以前的计算机里是没有 DMAC 的,所有数据都是由 CPU 来搬运的。随着对于数据传输的需求越来越多,先是出现了主板上独立的 DMAC 控制器。到了今天,各种 I/O 设备

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值