当前位置: 代码迷 >> 综合 >> (零)DPDK-基础概念
  详细解决方案

(零)DPDK-基础概念

热度:115   发布时间:2023-10-17 00:44:06.0

CPU 中的内存结构

 CPU 和各级缓存、内存、硬盘之间的关系 

(零)DPDK-基础概念

(零)DPDK-基础概念

缓存一致性:

目前结构每个core都会持有自己私有的L1/L2 Cache, 但 L3 cache是所有core共有的. 那么就存在一个cache一致性的问题 

(零)DPDK-基础概念

MESI 为了保证多个缓存中共享数据的一致性,定义了 cache line 的四种状态,而线程对 cache line 的四种操作可能会产生不一致的状态,

因此缓存控制器监听到本地操作和远程操作的时候,需要对地址一致的 cache line 状态进行一致性修改,从而保证数据在多个缓存之间保持一致性。(M: modified E: Exclusive S: shared I: invalid)

在 MESI 出现之前的解决缓存一致性的方案是总线锁机制,这种解决方案效率很低,锁住总线期间,其他 CPU 无法访问内存

  • Modified(M):  (唯一一份,和主存不一致)(所有其它core对这个cacheline的读操作必须在该cacheline 写回主存后, 写回主存后,状态变换到share
  • Exclusive(E): (唯一一份,干净的和主存一致),可以转换到shared 或者M,重点是可以转换到M,也就意味着可以对其修改!
  • Shared(S):    (不止一份,可能在多个core中,干净的和主存一致), 随时会被invalidate.
  • Invalid(I):      (无效)表示这个cacheline 目前不可用

(零)DPDK-基础概念

(零)DPDK-基础概念

当两个core同时执行针对同一地址的CAS指令时,其实他们是在试图修改每个core自己持有的Cache line, 假设两个core都持有相同地址对应cacheline,且各自cacheline 状态为S, 这时如果要想成功修改,就首先需要把S转为E或者M, 则需要向其它core invalidate 这个地址的cacheline,则两个core都会向ring bus 发出 invalidate这个操作, 那么在ringbus上就会根据特定的设计协议仲裁是core0,还是core1能赢得这个invalidate, 胜者完成操作, 失败者需要接受结果, invalidate自己对应的cacheline,再读取胜者修改后的值, 回到起点.

我们可以发现MESIF协议大大降低了读操作的时延,没有让写操作更慢,同时保持了一致性

执行序一致性:

CPU 中的乱序执行优化是处理器为提高运算速度而做出违背代码原有顺序的优化。虽然顺序变,但执行结果是不会变的。

乱序优化就是这样,为了提高效率 CPU 做出的优化,这在单核的时候是不会有问题的,但是在多核时代又会出现问题。

能不能乱序肯定有一套规则,这套规则也就是内存屏障

(零)DPDK-基础概念

从上可以看出 intel x86架构的CPU上,只有Store-Load乱序,没有其他乱序 

Store和Load指令的含义:简单来说,Store就是将处理器缓存中的数据刷新到内存中,Load则是从内存拷贝数据到缓存当中. 

内存屏障分类如下:

(零)DPDK-基础概念

StoreLoad Barriers同时具备其他三个屏障的效果,因此也称之为全能屏障,是目前大多数处理器所支持的,但是相对其他屏障,该屏障的开销相对昂贵.在x86架构的处理器的指令集中,lock指令可以触发StoreLoad Barriers. 

 

UIO/VFIO/DDIO

UIO

一个设备驱动的主要任务有两个:

1. 读写设备的内存 
2. 处理设备产生的中断

  • 第一个任务,UIO 核心实现了mmap()可以处理物理内存(physical memory),逻辑内存(logical memory),虚拟内存(virtual memory)。UIO驱动的编写是就不需要再考虑这些繁琐的细节。
  • 第二个任务,对于设备中断的应答必须在内核空间进行。所以在内核空间有一小部分代码用来应答中断和禁止中断,但是其余的工作全部留给用户空间处理

如果用户空间要等待一个设备中断,它只需要简单的阻塞在对 /dev/uioX的read()操作上。

  • 当设备产生中断时,read()操作立即返回。
  • UIO 也实现了poll()系统调用,你可以使用select()来等待中断的发生。select()的超时参数可以用来实现有限时间内等待中断

对设备的控制还可以通过/sys/class/uio下的各个文件的读写来完成。

  • 你注册的uio设备将会出现在该目录下。
  • 假如你的uio设备是uio0那么映射的设备内存文件出现在 /sys/class/uio/uio0/maps/mapX,对该文件的读写就是对设备内存的读写。

(零)DPDK-基础概念

UIO的几个特点:

  • 一个UIO设备最多支持5个mem和portio空间mmap映射。 
  • UIO设备的中断用户态通信机制基于wait_queue实现。
  • 一个UIO设备只支持一个中断号注册,支持中断共享。

总的来说,UIO框架适用于简单设备的驱动,因为它不支持DMA,不能支持多个中断线,缺乏逻辑设备抽象能力。

VFIO 

VFIO是一个可以安全的把设备I/O、中断、DMA等暴露到用户空间(userspace),从而可以在用户空间完成设备驱动的框架。用户空间直接设备访问,虚拟机设备分配可以获得更高的IO性能

UIO不支持DMA,所以通过DMA传输大流量数据的IO设备,如网卡、显卡等设备,无法使用UIO框架,VFIO做为UIO的升级版,主要就是解决了这个问题。

(零)DPDK-基础概念

  • IOMMU是一个硬件单元,它可以把设备的IO地址映射成虚拟地址,为设备提供页表映射,设备通过IOMMU将数据直接DMA写到用户空间。之所以不共用MMU单元,是为了保证和进程的页表相互独立,防止设备访问进程的任意地址空间。所以VFIO的IOMMU功能保障了安全的非特权级别的用户态设备驱动机制。
  •  /dev/vfio是一个设备文件,作为一个IOMMU设备的用户态呈现。
  • container是内核对象,表示一个IOMMU设备,是一个IOMMU设备的内核态呈现。所以在VFIO中,container是IOMMU操作的最小对象。
  • 在虚拟化场景下,一个物理网卡可能要虚拟成几个虚拟网卡,或者说虚拟功能设备(VF),这几个VF共用一个IOMMU,所以VFIO模型增加一个iommu_group的概念,用来表示共享同一个IOMMU的一组device。

VFIO的几个特点:

  • VFIO设备支持多中断号注册。
  • 设备的中断用户态通信机制基于eventfd/irqfd实现。用户通过/dev/vfio设备select/poll/epoll,从而实现中断从内核态到用户态的异步事件通知。
  • 支持对物理设备进行逻辑抽象。
  • 仅支持pci intx中断共享,其他类型中断不支持共享。
  • VFIO仅支持特定IOMMU设备,如x86与PowerPC平台的PCI设备和ARM平台的platform设备

DDIO

DDIO使得外部网卡和CPU通过LLC cache直接交换数据,绕过了内存,增加了CPU处理报文的速度

(零)DPDK-基础概念

TLB Cache 和 大页内存(Hugepages)

TLB

TLB(Translation Lookaside Buffer)称为页表缓冲,是一个存放着页表缓存(虚拟地址到物理地址的转换表)的内存管理单元,用于改进虚拟地址到物理地址转换速度。

TLB是内存里存放的页表的缓存,那么它里边存放的数据实际上和内存页表区的数据是一致的,在内存的页表区里,每一条记录虚拟页面和物理页框对应关系的记录称之为一个页表条目(Entry),同样地,在TLB里边也缓存了同样大小的页表条目(Entry)

1:TLB在X86体系的CPU里的实际应用最早是从Intel的486CPU开始的,在X86体系的CPU里边,一般都设有如下4组TLB:

  • 第一组:缓存一般页表(4K字节页面)的指令页表缓存(Instruction-TLB);
  • 第二组:缓存一般页表(4K字节页面)的数据页表缓存(Data-TLB);
  • 第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存(Instruction-TLB);
  • 第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存(Data-TLB);

2:TLB命中和TLB失败

  • 如果TLB中正好存放着所需的页表,则称为TLB命中(TLB Hit);
  • 如果TLB中没有所需的页表,则称为TLB失败(TLB Miss)。
  • 当CPU收到应用程序发来的虚拟地址后,
  • 首先到TLB中查找相应的页表数据,如果TLB中正好存放着所需的页表,则称为TLB命中(TLB Hit)
  • 接下来CPU再依次看TLB中页表所对应的物理内存地址中的数据是不是已经在一级、二级缓存里了,若没有则到内存中取相应地址所存放的数据。
     

Hugepages

Linux操作系统的大页内存,主要分为2M和1G大小。可以从CPU的标识中看出支持大内存页的类型

  • Linux在内存管理中采用受保护的虚拟地址模式,在代码中地址分为3类:逻辑地址、线性地址、物理地址
  • 程序使用具体内存简单说就是逻辑地址通过分段机制映射转化为线性地址,然后线性地址通过分页机制映射转化为物理地址的过程
  • 而在实际使用中,仅将线性地址映射为物理地址的过程中,需要从内存中读取至少四次页目录表(Page Directory)和页表 (Page Table),为了加快内核读取速度,CPU在硬件上对页表做了缓存,就是TLB。

线性地址先从TLB获取高速缓存内存,如果不存在就从内存表获取,如果有直接的映射,直接从内存读取,没有则产生缺页中断,从新分配物理内存,或者从硬盘上将swap读取。具体图示如下:

(零)DPDK-基础概念

普通页大小是每个4K,如果是4K页的寻址如下,使用物理内存时需要多级查找才能找到对应的内存

(零)DPDK-基础概念

4K的页表是linux针对一般情况得出的合适大小,然而对于特殊应用可以通过扩大页表面积提高内存使用效率 

dpdk使用hupage的思想就是让程序尽量独占内存防止内存换出,扩大页表提高hash命中率,通过hugage技术扩大了该使用的页表大小,设定为更适合高频内存使用程序的状态,获得了以下几点优势。

  • 无需交换。也就是不存在页面由于内存空间不足而存在换入换出的问题
  • 减少TLB负载。
  • 降低page table查询负载

 

CPU的物理核,逻辑核概念 CPU亲和性

  • 物理处理器封装个数 ---->物理CPU数: 几个CPU
  • 处理器核心数--->CPU核心数 : 一个CPU有几个core
  • 逻辑处理器数--->逻辑CPU数 : 四核八线程
  • SIBLING:内核认为的单个物理处理器所有的超线程个数  (SIBLING等于实际物理核数的话,就说明没有启动超线程,反之启用超线程。)
  • 超线程: 利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,采用超线程即是可在同一时间里,应用程序可以使用芯片的不同部分
  • CPU的亲和性---->cpu affinity机制: 也就是说把一个程序绑定到一个物理CPU上

 

NUMA-非一致性内存访问

(零)DPDK-基础概念

 传统架构

(零)DPDK-基础概念

互斥锁 mutex   :   只有一个保持着

自旋锁spinlock:   类似互斥锁    区别: 互斥等待睡眠 自旋原地循环等待

读写锁 rwlock  :   一个写多个读 但不能同时有读有写

RCU锁(Read-Copy Update):读-复制 更新   对读写锁的一种改进 

  • 读者不需要获得任何锁就可访问RCU保护的临界区;拷贝(Copy):写者在访问临界区时,写者“自己”将先拷贝一个临界区副本,然后对副本进行修改;更新(Update)

网卡

PCIe

PCI Express(Peripheral Component Interconnect Express)又称PCIe,它是一种高速串行通信互联标准。

PCIe规范遵循开放系统互联参考模型(OSI),自上而下分为事务传输层、数据链路层、物理层。

对于特定的网卡,PCIe一般作为处理器外部接口。

一般网卡采用DMA控制器通过PCIe Bus访问内存,除了对以太网数据内容的读写外,还有DMA描述符操作相关的读写,这些操作也由MRd/MWr来完成。

(零)DPDK-基础概念

PCIe包格式示例,对于一个完整的TLP包来说,除去有效载荷,额外还有24B的开销(TLP头部以16B计算)。 

(零)DPDK-基础概念

网卡DMA描述符环形队列

DMA(Direct Memory Access,直接存储器访问)是一种高速的数据传输方式,允许在外部设备和存储器之间直接读写数据。数据既不通过CPU,也不需要CPU干预。

整个数据传输操作在DMA控制器的控制下进行。网卡DMA控制器通过环形队列与CPU交互。

环形队列的内容部分位于主存中,控制部分通过访问外设寄存器的方式完成。 

(零)DPDK-基础概念

转发操作 

(零)DPDK-基础概念

Brust (突发)

Burst为一次完成多个数据包的收发,通过把收发包复杂的处理过程进行分解,打散成不同的相对较小的处理阶段,

把相邻数据访问、相似的数据运算集中处理,尽可能减少对内存或者低一级的处理器缓存的访问次数。(目的)

SIMD

即Single Instruction, Multiple Data,一条指令操作多个数据.是CPU基本指令集的扩展.主要用于提供fine grain parallelism,即小碎数据的并行操作.