CENTOS上的网络安全工具(三十)DPDK和HyperScan环境构建

一、预备知识

        由于DPDK涉及到强CPU相关的优化策略,以及对网卡驱动栈的替换,所以在开始之前,首先需要垫补点CPU相关的概念,以及Linux上和网卡驱动相关的管理命令。

(一)CPU架构及相关概念

1. Socket、Core和Node、lcore

        ①Socket ,物理概念。就是主板上的CPU插槽(座),一个插槽就是一个Socet。一般来说一个Socket可能对应一组物理资源,包括内存、总线等外围设备的通道。

        ②Core ,物理概念。一个物理的CPU核,独立的计算单元。多个CPU核可以属于一个物理的CPU,也就是说多个Core可以对应于一个Socket。

        ③Node ,逻辑概念。基本对应Socket。之所以说“基本”,是因为这两个是允许非1-1对应的。Node这个概念,创建出来感觉是为了对应NUMA架构的非对称内存访问,由于不同CPU核访问属于自己本地的内存和远程的内存的速度不一样,所以需要一个Node这样的概念,把使用一组本地内存的CPU核归为一类,从而可以用节点间距离(Node Distance)这样的概念来衡量/配置不同CPU核访问不同内存的相应速度。一般来说,由于现代内存管理器被继承到物理CPU当中,一个Socket一般对应一个内存管理器,其上的核从内存访问距离这个意思上来说,就应该属于一个Node。Socket与Node也可以多对1配置,即Node跨Socket,猜测还是和内存管理器有关——比如多个Socket共一个内存管理器,访问时间一样。这种多对一的情况确实见过,只不过这里我们没有这样的配置。

        ④lcore,逻辑/物理概念。逻辑核产生的原因大概是超线程吧,超线程技术通过为1个处理单位提供2份状态、总线资源,使得CPU在进行IO的时候可以并行指令操作,从而使1个物理核看起来像多个核心在工作一样。一般来说,较常见的是1个物理核包含2个逻辑核。

        我的虚拟机是4 CPU,每CPU 4核,1个Node节点。所以查看出来的node信息核socket(physical id)信息是这样的:

        基于Linux指令查看Node和lcore:

        在 /sys/devices/system/node/下(仍然声明以下,这一系列一直没有换操作系统,默认centOS 8 Stream),通过ls node*文件,可以查看当前机器有几个Node;通过对每个Node*文件夹ls,可以查看其下的lcore(这里被明明为cpu*),以及对应的内存空间情况。

        在 /sys/devices/system/下,也有一个明文cpulist的文件,其中也记录了本机的CPU列表状态。

[root@bogon usertools]# ls /sys/devices/system/node/node0
compact  cpu12  cpu3  cpu8      hugepages  memory11  memory16  memory20  memory32  memory37  memory41  memory46  memory50  memory55  memory6   memory64  memory69  memory9    vmstat
cpu0     cpu13  cpu4  cpu9      meminfo    memory12  memory17  memory21  memory33  memory38  memory42  memory47  memory51  memory56  memory60  memory65  memory7   numastat   x86
cpu1     cpu14  cpu5  cpulist   memory0    memory13  memory18  memory22  memory34  memory39  memory43  memory48  memory52  memory57  memory61  memory66  memory70  power
cpu10    cpu15  cpu6  cpumap    memory1    memory14  memory19  memory23  memory35  memory4   memory44  memory49  memory53  memory58  memory62  memory67  memory71  subsystem
cpu11    cpu2   cpu7  distance  memory10   memory15  memory2   memory3   memory36  memory40  memory45  memory5   memory54  memory59  memory63  memory68  memory8   uevent

         基于Linux指令查看物理核:

[root@bogon usertools]# cat /proc/cpuinfo |grep "physical id"|sort -u
physical id	: 0
physical id	: 1
physical id	: 2
physical id	: 3

2.UMA和NUMA

         现代服务器中通常有多路CPU,多个CPU对内存访问的争夺是一个需要精密管理的问题。

在传统的对称多处理器(SMP)架构中,所有的CPU对称工作,无主次从属关系,各CPU共享相同的物理内存。相应的,早期内存控制器没有集成到CPU内部,CPU通过前端总线(FSB:Front Side Bus)连接到北桥芯片再访问内存,内存控制器在北桥芯片中。这样,每个CPU访问内存所需的时间是相同的,因此SMP也被称为一致存储器访问结构 (UMA:Uniform Memory Access)。

        随着CPU芯片制造的快速发展,在一张主板上可以承载的CPU越来越多,CPU内的逻辑核也越来越多。这种情况下,它们对内存的访问会争抢内存总线,从而造成总线累死,CPU饿死的情况。于是出现了非一致性内存访问结构(NUMA)来解决这个问题。

        NUMA架构中,内存控制器被集成在CPU内部,一般一个CPU/Socket会有一个独立的内存控制器。每个CPU/Socket独立管理一部分内存,称为本地内存;CPU和CPU之前通过QPI(Quick Path InterConnect)总线进行连接,从而可以访问不和自己直接连接的远程内存。这种架构中,不同CPU访问内存的速度就出现了显著不同。

         安装并执行numactl 可以显示当前机器的numa信息

[root@bogon usertools]# yum list numactl
上次元数据过期检查:2:54:09 前,执行于 2024年09月21日 星期六 23时01分09秒。
已安装的软件包
numactl.x86_64                                                                                         2.0.16-4.el8   

         通过numctl --hardware显示numa信息

[root@bogon usertools]# numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 7665 MB
node 0 free: 3472 MB
node distances:
node   0 
  0:  10 

         其中node distance值不同cpu节点之间的内存访问距离,应该是一个对阵矩阵。这里因为只有1个node,所以只有1行1列。node 0 size 则指该组CPU被分配到8G内存(为该虚拟器的所有内存)。

        基于Linux命令将程序绑定在固定的核上运行 

        使用munactl命令,可以将程序绑定在指定的NUMA节点上运行,同时还可以指定该程序使用的内存控制器在哪个节点上。使用参数“--show”,可以查看当前shell所在的节点情况:

[root@bogon usertools]# numactl --show
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
cpubind: 0 
nodebind: 0 
membind: 0 
preferred: 

        如果要限制程序执行的NUMA节点或指定内存分配的策略,可如下方式:

        ①绑定sampleProgram程序到节点0、内存在节点1上运行:

                numactl --cpubind=0 --membind=1 sampleProgram args

        ②绑定sampleProgram在2,3,7,8上运行

                numactl --physcpubind=2-3,7-8 sampleProgram args

        ③在所有节点上轮询分配内存

                numactl --interleave=all sampleProgram args

        ④优先从节点1上分配内存

                numactl --preferred=1

        3. 基于lscpu指令总结CPU信息

        使用lscpu命令,可以更加直观的看到机器CPU的配置信息,这个numactl配合起来可以更全面的掌握DPDK配置时需要了解的系统硬件信息。比如我们的测试环境,是基于AMD CPU的物理机上的VMware,VMware上配置了4个4核CPU,所以socket=4,lcore(cpu)有16个,但是内存管理器只有一个,所以NUMA节点只有一个,所有的lcore都属于Node 0.

[root@bogon examples]# lscpu
架构:           x86_64
CPU 运行模式:   32-bit, 64-bit
字节序:         Little Endian
CPU:             16
在线 CPU 列表:  0-15
每个核的线程数: 1
每个座的核数:   4
座:             4
NUMA 节点:      1
厂商 ID:        AuthenticAMD
BIOS Vendor ID:  AuthenticAMD
CPU 系列:       25
型号:           33
型号名称:       AMD Ryzen 9 5950X 16-Core Processor
BIOS Model name: AMD Ryzen 9 5950X 16-Core Processor            
步进:           0
CPU MHz:        3393.610
BogoMIPS:       6787.22
超管理器厂商:   VMware
虚拟化类型:     完全
L1d 缓存:       32K
L1i 缓存:       32K
L2 缓存:        512K
L3 缓存:        32768K
NUMA 节点0 CPU: 0-15

(二)网络接口卡和驱动

        网络接口卡(NIC:Network Interface Card)是DPDK主要操作的硬件对象。在DPDK程序中,除了一上来的rte_eal_init函数会初始化与CPU、内存相关的一些参数以外,在DPDK程序开始处理网络数据之前,基本上所做的全是对网卡硬件的参数配置工作。所以,要顺利配置DPDK环境并进行编程开发,对系统网卡设备及其驱动的理解必不可少。

        1. PCIe

        PCIe指PCI Express,是一种计算机总线标准,用于在CPU和各类外设之间传输数据,所以也可以理解为计算机内部的“网络协议”。DPDK的优化策略的一个主要部分,就是如何充分利用PCIe的传输带宽。

        在PCIe中,每个连接到主板上的设备都有唯一的地址,被称为BDF(Bus、Device、Function) 的缩写。其中Bus是指PCIe总线编号(一个系统中可能存在多个PCIe总线),Device是指连接到该总线上的某个设备编号,Function是指同一个设备上不同功能的编号。在PCIe设备ID和驱动程序匹配过程中,使用Root BDF和设备的BDF地址来确认特定设备的位置和身份。其中Root BDF是指PCIe层次结构中最高层的设备(通常是主板)的BDF地址。BDF 的典型格式为DDDD:BB:dd.FF。其中,“DDDD” 是四位十六进制的域编号,“BB”是两位十六进制的总线编号,“dd” 是两位十六进制的设备编号,“FF” 是两位十六进制的功能编号。

        域编号:在大多数情况下,都是0000.

        总线编号:总线编号/PCI域号,用于区分不同的PCI域。
        设备编号:表示物理总线上被分配给 PCIe 设备的编号。这个编号是唯一的,它和同一总线上的其他设备的编号都不同。
        功能编号:表示在同一 PCIe 设备内部,每个功能被分配的编号。对于只有一个功能的设备,这个值通常为 0。而对于包含多个功能的设备,每个功能都被分配一个唯一的编号。

        使用Linux命令lspci可以列出系统中的PCI总线设备信息,包括设备的厂商信息、设备ID、驱动程序信息等。lspci命令通常在终端中执行,不需要特殊的权限。典型参数如:

        -D:显示PCI域号。

        -v:显示更详细的设备信息,包括设备的IRQ、I/O端口等。

        -nn:显示设备的厂商和设备ID。

        -k:显示设备对应的内核模块(驱动程序)信息。

        -s <slot>:仅显示指定PCI插槽的设备信息。

        在我的虚拟机上,使用slpci查看网卡设备的PCI插接情况,显示如下:

[root@bogon testdpdkfilter]# lspci -D -v |grep Ethernet
0000:03:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
	DeviceName: Ethernet0
	Subsystem: VMware VMXNET3 Ethernet Controller
0000:13:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
	DeviceName: Ethernet1
	Subsystem: VMware VMXNET3 Ethernet Controller

         可以看到虚拟机中有2张网卡,都是Vmware虚拟的VMXNET3网络控制器,均在0000号域,分别插在3号和13号总线上。BDF在配置DPDK网卡驱动和启动程序参数时都有作用,需要记住。

        和这个pci设备有关的配置,可以在/sys/bus/pci/devices下面找到,以0000:13:00.0为例:

[root@bogon testdpdkfilter]# ls /sys/bus/pci/devices
0000:00:00.0  0000:00:07.3  0000:00:15.0  0000:00:15.4  0000:00:16.0  0000:00:16.4  0000:00:17.0  0000:00:17.4  0000:00:18.0  0000:00:18.4  0000:02:00.0  0000:0b:00.0
0000:00:01.0  0000:00:07.7  0000:00:15.1  0000:00:15.5  0000:00:16.1  0000:00:16.5  0000:00:17.1  0000:00:17.5  0000:00:18.1  0000:00:18.5  0000:02:01.0  0000:13:00.0
0000:00:07.0  0000:00:0f.0  0000:00:15.2  0000:00:15.6  0000:00:16.2  0000:00:16.6  0000:00:17.2  0000:00:17.6  0000:00:18.2  0000:00:18.6  0000:02:02.0
0000:00:07.1  0000:00:11.0  0000:00:15.3  0000:00:15.7  0000:00:16.3  0000:00:16.7  0000:00:17.3  0000:00:17.7  0000:00:18.3  0000:00:18.7  0000:03:00.0

[root@bogon testdpdkfilter]# cd /sys/bus/pci/devices/0000:13:00.0
[root@bogon 0000:13:00.0]# ls
acpi_index            config                    d3cold_allowed  driver_override  irq            local_cpus      msi_bus      remove        resource   resource3  subsystem_device
ari_enabled           consistent_dma_mask_bits  device          enable           label          max_link_speed  numa_node    rescan        resource0  revision   subsystem_vendor
broken_parity_status  current_link_speed        dma_mask_bits   firmware_node    link           max_link_width  power        reset         resource1  rom        uevent
class                 current_link_width        driver          iommu_group      local_cpulist  modalias        power_state  reset_method  resource2  subsystem  vendor

         使用lshw命令,可以列出PCIe桥接器及其子设备的详细信息,以总线信息形式输出:

[root@bogon build]# lshw -class bridge -businfo
Bus info          Device          Class          Description
============================================================
pci@0000:00:00.0                  bridge         440BX/ZX/DX - 82443BX/ZX/DX Host bridge
pci@0000:00:01.0                  bridge         440BX/ZX/DX - 82443BX/ZX/DX AGP bridge
pci@0000:00:07.0                  bridge         82371AB/EB/MB PIIX4 ISA
pci@0000:00:07.3                  bridge         82371AB/EB/MB PIIX4 ACPI
pci@0000:00:11.0                  bridge         PCI bridge
pci@0000:00:15.0                  bridge         PCI Express Root Port
pci@0000:00:15.1                  bridge         PCI Express Root Port
……
……

         参考:pcie 的bdf 详细介绍,及用法实例、linux 查看pci设备信息命令详解_pcie bdf-CSDN博客

lspci详解-CSDN博客

        2. 网卡驱动

        DPDK要比系统自带的网络传输机制快的原因是使用了用户态轮询机制(PMD:Pool Mode Driver)来替代传统的中断处理机制。在x86体系结构中,为了传输网卡数据,一次终端处理需要完成保存CPU状态、运行终端服务程序、恢复CPU状态等多个步骤,全部过程需要300个处理器始终周期以上。而持续的数据包处理,将造成大量终端请求,从而大幅降低系统的处理效率。

        在轮询模式中,可以借助(DDIO:Direct Data I/O)技术直接将数据包保存到CPU Cache中,或者在没有DDIO技术支持的情况下,直接保存在内存中。应用程序只需要周期性轮询到达报文标志位,检测是否有新的数据需要处理即可。因为没有使用中断机制,从而节省了大量性能开销。

        根据官方描述(5. Linux 驱动程序 — 数据平面开发工具包 19.11.14 文档 (dpdk.org)),不同的PMD 可能需要不同的内核驱动程序才能正常工作。 根据所使用的 PMD,应加载相应的内核驱动程序 并绑定到网络端口。

        早期,DPDK需要绑定UIO驱动,一个小的内核模块,用于设置设备、将设备内存映射到用户空间和注册中断。 在许多情况下,Linux 内核中包含的 standard 模块 可以提供 UIO 能力。然而自DPDK 1.7后,开始提供VFIO支持并作为首选项,UIO仅作为备用选项保留。因此,后面我们也仅仅讨论VFIO。   VFIO是一款用于支持硬件设备想用户态透传的驱动程序,依赖于IOMMU保护。

        (1)IOMMU

        IOMMU (Input and Output Memory Management Unit),IO内存管理单元。其名称显然是由和CPU虚拟地址物理地址映射相关的MMU衍生而来。所以,其作用与原理,也可以参考MMU来理解:

        MMU是将CPU虚拟地址转换为内存物理地址的硬件单元。类似地,IOMMU是将设备地址(又称总线地址)转换为内存物理地址的单元。较常见的一种情况,比如一些设备的引脚数较少,导致其位数较低,无法寻址到整个物理内存空间。比如仅支持32位寻址的设备申请DMA时,如果内核为设备分配的DMA buffer的地址高于4GB(以下简称为“high buffer”),则设备将无法寻址。
引入IOMMU进行内存地址映射后,IOMMU可以在[0, 4GB)范围内分配一段与高地址buffer长度相同的地址空间,设备向IOMMU分配的这段空间写入,IOMMU就会将其映射到DMA对应的高地址空间内存,并通知CPU从高地址空间读取内容,反之亦然。这种映射操作,在IOMMU机制中被称为“sync”或“bounce”。

        (2)VFIO

        虚拟功能IO(VFIO:Virtual Function IO)是一套完整的用户态驱动(userspace driver)方案,用以将设备I/O、中断、DMA等功能穿透到用户空间。如上图,VFIO在IOMMU和PCI总线上进行了进一步封装,以此为虚拟机提供高性能的设备直通方案。VFIO基于VT-d/AMD-Vi技术提供的DMA Remapping和Interrupt Remapping特性(所以BIOS的VT-d选项必须打开),提供接近物理设备的I/O性能,为用户态进程直接访问硬件提供驱动支撑。

        vfio_iommu是VFIO对iommu层的统一封装主要用来实现DMA Remapping的功能,即管理IOMMU页表的能力。

        vfio_pci是VFIO对pci设备驱动的统一封装,它和用户态进程一起配合完成设备访问直接访问,具体包括PCI配置空间模拟、PCI Bar空间重定向,Interrupt Remapping等。

        (3)为DPDK配置VFIO

        综上,要使用DPDK,需要装载VFIO驱动;而装载VFIO驱动,需要打开VT-d支持,并且是能IOMMU。这决定了我们在启用DPDK前,需要对网卡驱动所作的所有准备工作:  

        ① BIOS中打开VT-d(略)

        ② 使能 IOMMU

         通过修改系统启动配置项,将IOMMU使能指令添加到启动命令行中:

        即在/etc/default/grub中,GRUB_COMLINE_LINUX这一行,后面加上 “iommu=pt intel_iommu=on”。

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap rhgb quiet iommu=pt intel_iommu=on"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true

        然后使用grub2-mkconfig指令生成GRUB2配置文件,更新GRUB2引导加载程序。更新完成后需要重启系统。

[root@bogon build]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
done
[root@bogon build]reboot

        成功的情况下,通过查看/pro/cmdline文件,会有“iommu=pt intel_iommu=on”存在。

[root@bogon ~]# cat /proc/cmdline |grep intel_iommu
BOOT_IMAGE=(hd0,msdos1)/vmlinuz-4.18.0-553.6.1.el8.x86_64 root=/dev/mapper/cs-root ro crashkernel=auto resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap rhgb quiet iommu=pt intel_iommu=on
[root@bogon ~]# 

        ③ 加载vfio-pci模块

        VFIO 内核模块从 3.6.0 版本开始包含在 Linux 内核中,并且通常默认存在。使用uname指令确认内核版本大于3.6.0

[root@bogon build]# uname -a
Linux bogon 4.18.0-553.6.1.el8.x86_64 #1 SMP Thu May 30 04:13:58 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

        使用lsmod指令查看是否存在vfio模块:

[root@bogon build]# lsmod |grep vfio
vfio_pci               61440  0
vfio_virqfd            16384  1 vfio_pci
irqbypass              16384  1 vfio_pci
vfio_iommu_type1       36864  0
vfio                   36864  2 vfio_iommu_type1,vfio_pci

        如果没有结果,需要使用modprobe指令加载。注意提前使能IOMMU,否则会挂载失败:

[root@bogon ~]# modprobe vfio
[root@bogon ~]# modprobe vfio-pci

[root@bogon ~]# lsmod|grep vfio_pci
vfio_pci               61440  0
vfio_virqfd            16384  1 vfio_pci
irqbypass              16384  1 vfio_pci
vfio                   36864  2 vfio_iommu_type1,vfio_pci
[root@bogon ~]# 

        需要注意的是,由于linux并不会默认加载vfio和vfio-pci(注意 - 和 _ 符号,它们确实不一样),所以每次启动时都需要将他们载入,我的选择是把这些命令都放到~/.bashrc中去。

        参考:

Linux x86-64 IOMMU详解(一)——IOMMU简介_imbux-CSDN博客

Linux驱动:VFIO概述(vfio/iommu/device passthrough)_linux vfio-CSDN博客

DPDK中使用VFIO的配置 - 范加索尔拉 - 博客园 (cnblogs.com)

DPDK的PMD(uio/igb_uio/vfio-pci/uio_pci_generic)_dpdk pmd-CSDN博客

        3. 接收端扩展(RSS:receive side scaling)

         前面提到,DPDK会将NIC与处于同一NUMA节点的CPU进行绑定,以获取最佳的内存性能。进一步,DPDK还可以对支持接收端扩展的NIC,实现每网络接口上多个接收RX发送TX队列,每个队列与一个逻辑核绑定,从而避免单个逻辑核无法支撑大带宽的网络数据流处理。当然,要实现这一点,需要网络接口卡及驱动能够支持RSS。

        RSS是一种网络驱动程序技术,也称为多队列接收,可在多处理器系统中的多个CPU多个逻辑核之间有效分配网络接收处理,基于网络硬件接收队列进行网络数据处理的负载均衡,从而允许多核并行处理入站网络流量。RSS可用于缓解单个逻辑核过载导致的接收中断处理瓶颈。RSS进行负载均衡的算法主要取决与网络驱动提供的hash算法。典型的hash函数将数据包的IP地址、上层协议号和端口(5个元组)的部分或全部作为键用来计算hash值,并根据hash值确定对应处理数据的逻辑核,以此实现负载均衡。

        VMware对其虚拟的网络设备使用的是VMXNET3驱动。根据DPDK官方的介绍:VMXNET3 适配器是 VMware* ESXi 推出的下一代半虚拟化 NIC。 它专为性能而设计,提供 VMXNET2 中提供的所有功能,并添加了一些新功能,例如, 多队列支持(也称为接收方扩展、RSS)、 IPv6 卸载和 MSI/MSI-X 中断交付。 

        虽然提及VMXNET3驱动支持接收端扩展,但这种支持似乎并不是完整的:

         不知道这个队列之间基于RSS的负载均衡放在基于MAC地址的过滤下面,是不是说RSS Hash只支持针对MAC?如果是的话可能能够解释后面我们编写多队列程序时总是只有1个收包队列工作的情况——毕竟MAC地址都来自宿主机。

        使用 ethtool命令可以查看当前网络驱动的版本:

[root@bogon build]# ethtool -i ens160
driver: vmxnet3
version: 1.7.0.0-k-NAPI
firmware-version: 
expansion-rom-version: 
bus-info: 0000:03:00.0
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: yes
supports-priv-flags: no

      而且从DPDK官方给出的驱动支持功能表看,VMXNET3对RSS的支持似乎确实有限。 

        之所以在这里用了这么些篇幅提及RSS,是因为DPDK程序在初始话网卡参数的时候,通常会使能RSS;但在我们的虚拟环境中,使能RSS是可以成功的,只是在我们的示例程序(Run TO Complete模式)执行的时候,数据包总是被1个队列分配到,导致其余队列总是分不到流量。所以在这里花了写时间寻找原因。猜想是因为Hash算法的计算对象是MAC所致,没有深究,如有错误请指正。

        另一个,也有网文提到,通过查看/proc/interrupts文件,如果看到同一NIC对应多行的,就是支持RSS。比如VMXNET3,cat /proc/interrupts,这样大概就是支持的:

         但是挂载VFIO以后,就只剩一行了。也许这才是我们在DPDK中无法实现RSS的原因?因为VFIO不支持?:

参考:

61. 半虚拟 VMXNET3 NIC 的轮询模式驱动程序 — 数据平面开发工具包 24.11.0-rc0 文档 (dpdk.org)

1. Overview of Networking Drivers — Data Plane Development Kit 24.07.0 documentation (dpdk.org)

(三)大页内存

        DPDK在优化中,也大量使用了大页内存技术,来避免页表失效导致的CPU指令周期浪费——通过使用 hugepage 分配,需要的页面更少,翻译后备缓冲区(TLB、高速翻译缓存)更少, 这减少了将虚拟页面地址转换为物理页面地址所需的时间。 如果没有大页面,标准 4k 页面大小会出现高 TLB 未命中率,从而降低性能。因此,要使DPDK正常工作,配置大页内存是不可或缺的。

        正如DPDK官方说明:用于数据包缓冲区的大型内存池分配需要 Hugepage 支持 ,所以所用Linux操作系统的内核必须启用大页内存选项:

        还好这个选项常见操作系统都是打开(支持的),无需我们操心。但是在DPDK程序能够启动前,我们还是需要做一些配置工作,以为其保留大页面内存以供使用。设置大页内存,有动态设置和静态设置2种办法:

       1. 动态设置方法 

        学习测试的情况下,推荐动态设置的方法,因为简单高效。但是在某些内核版本下,不允许在运行时保留1GB的大页,那么只能通过静态方法在引导时就保留他们:      

        动态挂载情况下,仅需要将所需要的页的个数通过echo方法输入到nr_hugepages文件即可,页面大小则是选择系统默认的2MB:

        ① 没有关联的NUMA节点的情况

        对于双插槽 NUMA 系统, 引导时保留的 hugepage 数量通常在两个套接字之间平均分配,比如下面这样,可能会在每个NUMA节点上保留512个页面。

echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

        ② 有关联的NUMA节点的情况

        如果有关联NUMA节点,可以通过如下方法对每个不同的NUMA节点进行强制保留相对应的页面数量。

echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

        在我的系统上,是双管齐下的,因为我只有一个NUMA节点,所以大约效果是一样的:

[root@bogon usertools]# echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
[root@bogon usertools]# echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
[root@bogon usertools]# 

        因为是动态的,所以每次系统重启该指令都会失效,同样用~/.bashrc方法应对。

        2. 静态设置方法

        从DPDK官方的描述看,是推荐静态方法的。因为:在一般情况下,在运行时保留 hugepage 是完全可以的。 但在需要大量物理连续内存的使用案例中, 最好在引导时保留 hugepages, 因为这将有助于防止物理内存变得严重碎片化。

        要在引导时保留 hugepages,需要在内核命令行上向 Linux 内核传递一个参数——其实也就是和上面的引导操作更改一样,在/etc/default/grup中的GRUB的LINUX_CMD_LINE一行后面继续添加相应的设置命令:

[root@bogon usertools]# cat /etc/default/grub 
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap rhgb quiet iommu=pt intel_iommu=on default_hugepagesz=1G hugepagesz=1G hugepages=4"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true

        比如,如果只是想和上述动态的设置一样,对于2MB的默认大页大小,只需要将大页数量参数传递给kernel,如:

        hugepages=1024

        对于其它的页大小,如1G,需要同时设置hugepagesz和default_hugepagesz参数,如:

        default_hugepagesz=1G hugepagesz=1G hugepages=4

        3. 多进程模式下的大页内存配置

        DPDK提供从线程到进程级别的并发工作模型支持。  

      ① 在线程级别上,根据按照处理对象还是按照处理环节进行并行化的思路,将Network Processor(网络处理器)转发的模型分 为 run to completion ( RTC ) 模 型 和pipeline(流水线)模型2种:

        run to completion模型 结构上较为简单,简单的DPDK程序通常会采用。一般来说一个数据包的处理流程,囊括接收、解析到分发的多个阶段,如果以会话和数据包为单位来分配任务,即一个会话或数据包会交给一个工作线程(逻辑核)去处理,从而一个逻辑核就需要完成这个处理流程“从开始到结束”的所有工作,即run to completion。在SMP架构多核CPU上,我们可以通过在多个核上执行一样逻辑的程序,从而获得灵活的水平扩展能力,以此提高单位时间内事务处理的量。

        run to completion 模型是简单易扩展的,但并不能做到在任何场景下都高效。考虑这么一种场景(其实也挺经常见):如果不同的会话根据一个约定的Hash算法被分到不同的逻辑核,但有的会话长,有的会话短,有的会话解析复杂,有的简单,从而有可能会造成部分逻辑核拥塞丢包,部分逻辑核空闲的情况。这时使用run to completiong模型就很难调整适应了。

       pipeline模型 借鉴于工业上的流水线模型,将一个功能(大于模块级的功能)分解成多个独立的阶段,不同阶段间通过队列传递产品。这样,对于一些CPU密集和I/O密集的应用,通过pipeline模型,我们可以把CPU密集的操作放在一个微处理引擎上执行,将I/O密集的操作放在另外一个微处理引擎上执行。通过过滤器可以为不同的操作分配不同的线程,通过连接两者的队列匹配两者的处理速度,从而达到最好的并发效率。

         ② 在进程级别上,在 DPDK旨在允许一组 DPDK 进程 以简单透明的方式协同工作以执行数据包处理, 或其他工作负载。DPDK基于共享内存的访问为进程间通信(IPC)提供支持,每个DPDK进程在应用程序使用的hugepage内存上可以被分配不同的权限。最常见的多进程组织方法将进程的任务属性划分为2种:

        主进程 (Primary Process) 拥有共享内存的所有权限,具有初始化权限,通常被用于执行参数配置、资源分配、内存管理等初始化操作;

        辅进程(Secondary Process) 不具有初始化共享内存的权限,但是能访问已初始化的共享内存和在其中创建对象,通常被用于工作者进程,进行数据包的转发处理。

        为支持这两种进程类型,EAL提供了附加的命令行:

            --proc-type: 为进程指定类型,是主或辅。

             --file-prefix:允许不想合作的进程有不同的内存区域。

        需要注意的是,如果要采用多进程模式,则需要为大页内存创建一个映射点。现代Linux操作系统中,通常会有一个默认的,在/dev/hugepages下。该mount使用默认页大小,使用诸如上面动态配置中的方法可设置页面数量参数。或者换句话说,只要不改变默认大页大小,多进程情况下也可以使用动态设置方法完成大页内存设置。但如果要使用非默认的大页大小,则需要按照官方给出的方法,手工mount一下大页内存:

         /etc/fstab记录文件系统挂载信息。  磁盘被手动挂载之后都必须把挂载信息写入/etc/fstab这个文件中,系统开机时会主动读取/etc/fstab这个文件中的内容,根据文件里面的配置挂载磁盘,否则下次开机启动时仍然需要重新挂载。

        /etc/fstab中每行信息包含6个要素,从左到右依次为Device、Mount point、filesystem、parameters、dump、fsck。其中:

        Device为磁盘设备文件或者该设备的Label或者UUID。 Label就是分区的标签,在最初安装系统是填写的挂载点就是标签的名字。可以通过查看一个分区的superblock中的信息找到UUID和Label name:如执行dumpe2fs -h /dev/sda1,或blkid /dev/sda1。

        Mount point指向备的挂载点,即要挂载的目录。

        Filesystem为磁盘文件系统的格式,包括ext2、ext3、reiserfs、nfs、vfat等,这里是hugetlbfs。

        Parameters指文件系统的参数,这里是pagesize=1GB。

        需要注意的是nodev在这里不是设备文件,是指示“不解释字符或块文件系统上的指定设备”的含义。在Linux上,一些安全策略会要求,除了根目录(/),其他目录如果挂载了单独的分区,应该添加 nodev 挂载选项,以避免通过创建伪设备挂载的方式非法访问文件系统。

        4. 直接使用dpdk 工具

        当然,也可以使用dpdk安装目录下usertools中的dpdk-hugepages.py工具进行大页内存的设置,该工具有许多命令行选项:

dpdk-hugepages [options]

-h,--help                               显示使用信息并退出
-s,--show                              打印当前巨页配置
-c driver,--clear                     清除现有的大页面预留
-m,--mount                           挂载大页面文件系统
-u,--unmount                        卸载大页面文件系统
-n NODE,--node=NODE      设置 保留大页的NUMA 节点
-p SIZE,--pagesize=SIZE     选择要使用的大页大小,如果未指定,则使用默认大小。
-r SIZE,--reserve=SIZE        保留大页,大小以带有 K、M 或 G 后缀的字节为单位。
----setup SIZE                      清除、卸载、保留和安装的捷径。

        5. 大页内存的查看和管理

        除了上述使用dpdk-hugepages.py工具携带-s参数进行查看外,        

        还可以通过/proc/meminfo查看:

[root@bogon usertools]# cat /proc/meminfo
MemTotal:        7849632 kB
MemFree:         1464368 kB
MemAvailable:    3780764 kB
Buffers:            4400 kB
Cached:          2470484 kB
SwapCached:            0 kB
Active:          1198964 kB
Inactive:        2320656 kB
……  ……
Percpu:            82432 kB
HardwareCorrupted:     0 kB
AnonHugePages:    739328 kB
ShmemHugePages:        0 kB
ShmemPmdMapped:        0 kB
FileHugePages:         0 kB
FilePmdMapped:         0 kB
HugePages_Total:    1024
HugePages_Free:     1024
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
…… ……

        或使用cat /proc/mounts |grep "huge",可查看挂载点mount情况

[root@bogon usertools]# cat /proc/mounts | grep "huge"
cgroup /sys/fs/cgroup/hugetlb cgroup rw,seclabel,nosuid,nodev,noexec,relatime,hugetlb 0 0
hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime,pagesize=2M 0 0

        如上可以看出,由于使用动态配置方法,且使用了默认的映射点,所以我们的挂载是在/dev/hugepages上。

        参考:2. System Requirements — Data Plane Development Kit 24.07.0 documentation (dpdk.org)2.3节

深入浅出DPDK学习笔记(6)———报文转发_run to completion-CSDN博客

(四)DPDK Example参数说明

        在DPDK所有的示例程序中,命令行参数分为两个部分,以“ -- ”为界,前面的为EAL通用参数,后面的则是每个example自己的参数。之所以如此,是借用了系统对命令行参数解析的一个特点,即解析到“ -- ”就会结束。从而,如果让系统自行解析,就恰好能解析到所有EAL通用参数。所以,我们通常会在DPDK的程序中看到,初始化时使用

ret = rte_eal_init(argc, argv);

        直接解析,这时,实际上使用的就是前半部分EAL通用参数;而后,一些初始化程序会将argc减去识别的通用参数数量,并将argv的指针向后移动到“ -- ”后面,这时再解析就是示例程序自己的参数了。

        1. EAL通用参数

        常用的EAL通用参数包括逻辑核(线程)、设备、多进程和存储相关多种类型:

        (1) 逻辑核(线程)相关

        -c <core mask> 
        
使用16进制掩码设置程序所使用的逻辑核,如 -c 0x1F,代表b00011111,从右到左依次代表核0-7。

        -l <core list>
        
列出使用的cores,参数格式是< c1 > [c2] [, c3 ,…],指定使用的核的列表。

        --lcores <core map>
        
lcores指一个EAL线程,这个参数的作用就是将每个EAL线程和物理cpu核进行绑定,参数格式比较复杂,举例说明:“--lcores='1,2@(5-7),(3-5)@(0,2),(0,6),7-8'”中,由“,”分给的每一组,采用lcores[@cpus]这样的格式,当逻辑核编号与CPU编号一一对应时,[@cpus]可以省略。所以如上例设定的对应关系为:

        lcore1    运行于        cpu1
        lcore2    运行于        cpu5
        lcore2    运行于        cpu6
        lcore2    运行于        cpu7
        lcore3    运行于        cpu0
        lcore4    运行于        cpu1
        lcore5    运行于        cpu2
        lcore0    运行于        cpu0
        lcore6    运行于        cpu6
        lcore7    运行于        cpu7
        lcore8    运行于        cpu8

        注:-c 和 -l/--lcores参数,只能选择其中一种

        --master-lcore <core ID>
        指定master 线程运行在<core ID>上。dpdk线程分为 master, slave 两种类型,一般 master 做管理相关的,slave 是真正处理业务的线程。

        -s <service core mask>
        
十六进制位掩码指定哪些cpu 核作为slave线程(服务线程)。

        (2) 设备相关

        -b, --pci-blacklist <[domain:]bus:device.func>
        设置设备黑名单,EAL不会使用黑名单中的设备,可以使用多个-b参数。

        -w, --pci-whitelist <[domain:]bus:devid.func>
        添加一个pci设备到白名单中。-b和-w参数不能同时出现。

        --vdev <device arguments>
        添加一个虚拟设备,格式如 <driver><id>[,key=val, ...],似乎是可以将pcap文件虚拟为网络接口设备。

--vdev 'net_pcap0,rx_pcap=input.pcap,tx_pcap=output.pcap'

        -d <path to shared object or directory>
        加载额外驱动,参数可以是一个驱动文件或是包含多个驱动文件的文件夹。可以使用多个-d选项。

        --no-pci
        禁止pci总线

        (3) 多进程相关

        如前所述,独立运行的DPDK进程只能是主进程,而副进程只能在主进程已经配置了hugepage共享内存之后运行。为了支持这两种进程类型,以及稍后描述的其他多进程设置,可以使用两个额外的命令行参数。

        --proc-type:
        
指定一个dpdk进程是主进程还是副进程(参数值就用上面的primary或是secondary,或者是auto)。

        --file-prefix:
        
允许非合作的进程拥有不同的内存区域。
        (--file-prefix,主副进程默认文件路径/var/run/.rte_config,同一个组的主副进程使用相同的参数,如果想运行多个主进程,这个参数就必须指定)。

        (4)存储相关

         -n <number of channels>
        设置每个处理器socket的内存通道数。不那么严格地理解,就是在每个CPU插槽的周围,有几路内存插槽(比如4个插槽,但需要一对一对插,那应该是2路通道)。若是需要严格的对应,可以参考下面的参考链接。

        -r <number of ranks>
        设置内存ranks数(默认自动检测),对应插了几条内存。该参数严格的说和内存的DIMM / SIMM类型有关。

        -m <megabytes>
        设置启动时与分配存储数量。

        --in-memory
        不要创建任何共享数据结构,完全运行在存储中。暗指--no-shconf 和--huge-unlink。

        --iova-mode <pa|va>
        强制设置IOVA模式为指定值。

        参考:

28.Multi-process Support_dpdk多进程实例multi-process-CSDN博客

Memory中的Channel/Rank/Bank解析_--内存ce阈值(rank): 内存ce阈值(bank): --内存ce阈值(row): 关闭-CSDN博客

7. EAL parameters(dpdk参数介绍)_dpdk 参数-CSDN博客

         2. 各Example参数

        以l2fwd为例,通过程序源码可以找到各example解析参数及打印参数含义的部分代码。不过,我没有找到example自身参数帮助的打印方法——比如"-h"参数,但是可以随便设置一个错误的option诱导example打印出帮助来:

[root@bogon examples]# ./dpdk-l2fwd -- -help
EAL: Detected CPU lcores: 16
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: VFIO support initialized
EAL: Using IOMMU type 8 (No-IOMMU)
EAL: Ignore mapping IO port bar(3)
EAL: Probe PCI driver: net_vmxnet3 (15ad:07b0) device: 0000:13:00.0 (socket -1)
TELEMETRY: No legacy callbacks, legacy socket not created
./dpdk-l2fwd: invalid option -- 'h'
./dpdk-l2fwd [EAL options] -- -p PORTMASK [-P] [-q NQ]
  -p PORTMASK: hexadecimal bitmask of ports to configure
  -P : Enable promiscuous mode
  -q NQ: number of queue (=ports) per lcore (default is 1)
  -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)
  --no-mac-updating: Disable MAC addresses updating (enabled by default)
      When enabled:
       - The source MAC address is replaced by the TX port MAC address
       - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID
  --portmap: Configure forwarding port pair mapping
	      Default: alternate port pairs

EAL: Error - exiting with code: 1
  Cause: Invalid L2FWD arguments

        如上,可以看到对于l2fwd:

        -p        为网络接口掩码,如果要用两块网卡,该值应该设为3

        -q        每逻辑核对应的队列数量(等同于接口数量,默认为1)

        -P        使能混杂模式

        这3个参数的含义,在大多exmaple里面是一样的。

(五)ldconfig与动态链接库

        dpdk和hyperscan编译后,都会形成第三方的库文件,这些文件需要在dpdk、hyperscan程序运行的时候加载,这需要用到ldconfig管理工具。

        ldconfig的作用,主要是在默认搜寻目录/lib和/usr/lib以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件。缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。linux下的共享库机制采用了类似高速缓存机制,将库信息保存在/etc/ld.so.cache,程序连接的时候首先从这个文件里查找,然后再到ld.so.conf的路径中查找。为了让动态链接库为系统所共享,需运行动态链接库的管理命令ldconfig,此执行程序存放在/sbin目录下。

        如果动态库已经在/lib和/usr/lib下,仅执行ldconfig就行。比如:

[root@bogon boost]# ls /usr/lib64 |grep boost
libboost_atomic.so.1.66.0
libboost_chrono.so.1.66.0
libboost_date_time.so.1.66.0
libboost_filesystem.so.1.66.0
libboost_iostreams.so.1.66.0
libboost_program_options.so.1.66.0
libboost_random.so.1.66.0
libboost_regex.so.1.66.0
libboost_system.so.1.66.0
libboost_thread.so.1.66.0
libboost_timer.so.1.66.0

        否则(即安装程序没有帮我们搬,那我们就得告诉ldconfig,这些库在那个位置)。例如,dpdk和hyperscan都没有将自己的库放置在/lib或/usr/lib下,而是在/usr/local/lib(or lib64)下——这个可以通过dpdk和hyperscan的pkg-config *.pc文件找到(见后文)。这种情况下,直接使用ldconfig是找不到库的:

[root@bogon boost]# ldconfig -v| grep libhs

        所以,需要先将库文件的路径追加入/etc/ld.so.conf中,再刷新ldconfig,就可以看见了:

[root@bogon boost]# echo "/usr/local/lib64" >> /etc/ld.so.conf
[root@bogon boost]# echo "/usr/local/lib" >> /etc/ld.so.conf
[root@bogon boost]# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
/usr/local/lib64
/usr/local/lib
[root@bogon boost]# ldconfig
[root@bogon boost]# ldconfig -v |grep libhs
	libhs.so.5 -> libhs.so.5.4.2
	libhs_runtime.so.5 -> libhs_runtime.so.5.4.2

        初始的/etc/ld.so.conf如下:

[root@bogon l2fwd]# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf

        可搜寻的动态库可以通过执行ldconfig -v指令查看:

[root@bogon l2fwd]# ldconfig -v
/usr/lib64/dyninst: (from /etc/ld.so.conf.d/dyninst-x86_64.conf:1)
	libsymtabAPI.so.12.1 -> libsymtabAPI.so.12.1.0
	libsymLite.so.12.1 -> libsymLite.so.12.1.0
	libstackwalk.so.12.1 -> libstackwalk.so.12.1.0
	libpcontrol.so.12.1 -> libpcontrol.so.12.1.0
	libpatchAPI.so.12.1 -> libpatchAPI.so.12.1.0
	libparseAPI.so.12.1 -> libparseAPI.so.12.1.0
	libinstructionAPI.so.12.1 -> libinstructionAPI.so.12.1.0
	libdyninstAPI_RT.so.12.1 -> libdyninstAPI_RT.so.12.1.0
	libdyninstAPI.so.12.1 -> libdyninstAPI.so.12.1.0
	libdynElf.so.12.1 -> libdynElf.so.12.1.0
	libdynDwarf.so.12.1 -> libdynDwarf.so.12.1.0
……
……
……

        当然,如果不打算配置ld.so.conf,还有一种更为传统的办法用来指示动态链接库的查找路径,就是配置 LD_LIBRARY_PATH 环境变量:

[root@bogon pkgconfig]# echo $LD_LIBRARY_PATH
:/opt/dpdk/build/lib:/opt/hyperscan/mybuild/lib

        只不过这个每次重启都会失效,需要放在在~/.bashrc中启动时执行。 

二、DPDK的编译与部署

        进入版本20以后,DPDK的编译部署使用了meson和pkg-config,很大程度上简化了这一过程的难度。我们还是以VMWare上的CentOS Stream 8作为基础环境,开始搭建。首先碰到了,仍然是CentOS镜像库失效的问题。

(一)更改国内CentOS Steam 8镜像 

[root@localhost ~]# dnf groupinstall "Development Tools"
CentOS Stream 8 - AppStream                                                                                                                                                        0.0  B/s |   0  B     00:48    
Errors during downloading metadata for repository 'appstream':
  - Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8-stream&arch=x86_64&repo=AppStream&infra=stock [Could not resolve host: mirrorlist.centos.org]
错误:为仓库 'appstream' 下载元数据失败 : Cannot prepare internal mirrorlist: Curl error (6): Couldn't resolve host name for http://mirrorlist.centos.org/?release=8-stream&arch=x86_64&repo=AppStream&infra=stock [Could not resolve host: mirrorlist.centos.org]

        CentOS虽然部分断更了,但没听说Centos Stream 8的镜像库都停了。总之就是如上图所示,刚装上的系统就连不上镜像库,动用魔法也不好使。

        更换成阿里镜像源。首先到 /etc/yum.repos.d下备份原有repo文件——其实都删了也没事,反正连不上。

[root@localhost yum.repos.d]# mkdir bak
[root@localhost yum.repos.d]# mv *.repo bak/ 

        下载阿里镜像源的repo文件。wget的参数从左到右分别指示:递归下载,不追溯父目录,不创建目录,逗号分割可接收的扩展名列表(这里只有1个,repo),运行命令“robots=off”,不验证服务器证书,当选择本地文件名时允许 Content-Disposition。最后是下载地址URL。

        简单说,就是到下载URL上下载所有.repo文件到本地目录。

[root@localhost yum.repos.d]# wget -r -np -nd -A ".repo" -e robots=off --no-check-certificate --content-disposition https://mirrors.aliyun.com/repo/centos-stream/8/
--2024-09-21 09:02:02--  https://mirrors.aliyun.com/repo/centos-stream/8/
正在解析主机 mirrors.aliyun.com (mirrors.aliyun.com)... 60.9.0.234, 60.9.0.233, 60.9.0.231, ...
正在连接 mirrors.aliyun.com (mirrors.aliyun.com)|60.9.0.234|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:未指定 [text/html]
正在保存至: “index.html.tmp”
…… ……

        然后更新一下yum就可以了

[root@localhost yum.repos.d]# yum upgrade  -y

(二)安装编译环境

        1. 安装开发包套件

        首先是基础的程序开发套件,主要是需要包含gcc、gcc-c++和python之类的编程工具。

[root@localhost yum.repos.d]# yum groupinstall "Development Tools" -y
上次元数据过期检查:0:02:49 前,执行于 2024年09月21日 星期六 09时13分32秒。
依赖关系解决。

        2. pkg-config

        Development Tools会将pkg-config也安装好。pkg-config是一个linux下的编译依赖工具,可以用于获得某一个库/模块的所有编译相关的信息。比如执行"pkg-config libdpdk -libs -cflags",得到的输出几乎就可以直接用于gcc编译器的参数列表,里面包括了头文件包含目录和动态静态库目录等一切必要的信息:

[root@bogon lib]# pkg-config libdpdk -libs -cflags
-I/usr/local/include -include rte_config.h -march=native -mrtm -Wl,--as-needed -L/usr/local/lib64 -lrte_node -lrte_graph -lrte_pipeline -lrte_table -lrte_pdump -lrte_port -lrte_fib -lrte_pdcp -lrte_ipsec -lrte_vhost -lrte_stack -lrte_security -lrte_sched -lrte_reorder -lrte_rib -lrte_mldev -lrte_regexdev -lrte_rawdev -lrte_power -lrte_pcapng -lrte_member -lrte_lpm -lrte_latencystats -lrte_jobstats -lrte_ip_frag -lrte_gso -lrte_gro -lrte_gpudev -lrte_dispatcher -lrte_eventdev -lrte_efd -lrte_dmadev -lrte_distributor -lrte_cryptodev -lrte_compressdev -lrte_cfgfile -lrte_bpf -lrte_bitratestats -lrte_bbdev -lrte_acl -lrte_timer -lrte_hash -lrte_metrics -lrte_cmdline -lrte_pci -lrte_ethdev -lrte_meter -lrte_net -lrte_mbuf -lrte_mempool -lrte_rcu -lrte_ring -lrte_eal -lrte_telemetry -lrte_kvargs -lrte_log 
[root@bogon lib]# 

       那么这些pkg-config是如何获得这些信息的呢?一般来说,它们来自于安装某个库或者模块的时候,被同时写入到系统中的*.pc文件。这些pc文件可能会写到2个目录下:
        ① 系统的共享库目录 /usr/lib下
        ② PKG_CONFIG_PATH环境变量所指向的路径下

        类似DPDK和HYPERSCAN这样的库,因为都使用了pkg-config辅助构建编译依赖,所以也会将*.pc文件写入到PKG_CONFIG_PATH环境变量所指向的路径下:

[root@bogon /]# cat $PKG_CONFIG_PATH
cat: /usr/local/lib64/pkgconfig/: 是一个目录
[root@bogon /]# ls $PKG_CONFIG_PATH
libdpdk-libs.pc  libdpdk.pc  libhs.pc

        以libdpdk.pc和libhs.pc为例:

[root@bogon /]# cat $PKG_CONFIG_PATH/libdpdk.pc
prefix=/usr/local
includedir=${prefix}/include
libdir=${prefix}/lib64

Name: DPDK
Description: The Data Plane Development Kit (DPDK).
Note that CFLAGS might contain an -march flag higher than typical baseline.
This is required for a number of static inline functions in the public headers.
Version: 23.11.2
Requires: libdpdk-libs
Requires.private: libarchive, libcrypto, zlib, libpcap, libelf
Libs.private: -Wl,--whole-archive -L${libdir} -l:librte_common_cpt.a -l:librte_common_dpaax.a -l:librte_common_iavf.a -l:librte_common_idpf.a -l:librte_common_octeontx.a -l:librte_bus_auxiliary.a -l:librte_bus_cdx.a -l:librte_bus_dpaa.a -l:librte_bus_fslmc.a -l:librte_bus_ifpga.a -l:librte_bus_pci.a -l:librte_bus_platform.a -l:librte_bus_vdev.a -l:librte_bus_vmbus.a -l:librte_common_cnxk.a -l:librte_common_nfp.a -l:librte_common_qat.a -l:librte_common_sfc_efx.a -l:librte_mempool_bucket.a -l:librte_mempool_cnxk.a -l:librte_mempool_dpaa.a -l:librte_mempool_dpaa2.a -l:librte_mempool_octeontx.a -l:librte_mempool_ring.a -l:librte_mempool_stack.a -l:librte_dma_cnxk.a -l:librte_dma_dpaa.a -l:librte_dma_dpaa2.a -l:librte_dma_hisilicon.a -l:librte_dma_idxd.a -l:librte_dma_ioat.a -l:librte_dma_skeleton.a -l:librte_net_af_packet.a -l:librte_net_ark.a -l:librte_net_atlantic.a -l:librte_net_avp.a -l:librte_net_axgbe.a -l:librte_net_bnx2x.a -l:librte_net_bnxt.a -l:librte_net_bond.a -l:librte_net_cnxk.a -l:librte_net_cpfl.a -l:librte_net_cxgbe.a -l:librte_net_dpaa.a -l:librte_net_dpaa2.a -l:librte_net_e1000.a -l:librte_net_ena.a -l:librte_net_enetc.a -l:librte_net_enetfec.a -l:librte_net_enic.a -l:librte_net_failsafe.a -l:librte_net_fm10k.a -l:librte_net_gve.a -l:librte_net_hinic.a -l:librte_net_hns3.a -l:librte_net_i40e.a -l:librte_net_iavf.a -l:librte_net_ice.a -l:librte_net_idpf.a -l:librte_net_igc.a -l:librte_net_ionic.a -l:librte_net_ixgbe.a -l:librte_net_memif.a -l:librte_net_netvsc.a -l:librte_net_nfp.a -l:librte_net_ngbe.a -l:librte_net_null.a -l:librte_net_octeontx.a -l:librte_net_octeon_ep.a -l:librte_net_pcap.a -l:librte_net_pfe.a -l:librte_net_qede.a -l:librte_net_ring.a -l:librte_net_softnic.a -l:librte_net_tap.a -l:librte_net_thunderx.a -l:librte_net_txgbe.a -l:librte_net_vdev_netvsc.a -l:librte_net_vhost.a -l:librte_net_virtio.a -l:librte_net_vmxnet3.a -l:librte_raw_cnxk_bphy.a -l:librte_raw_cnxk_gpio.a -l:librte_raw_dpaa2_cmdif.a -l:librte_raw_ntb.a -l:librte_raw_skeleton.a -l:librte_crypto_bcmfs.a -l:librte_crypto_caam_jr.a -l:librte_crypto_ccp.a -l:librte_crypto_cnxk.a -l:librte_crypto_dpaa_sec.a -l:librte_crypto_dpaa2_sec.a -l:librte_crypto_nitrox.a -l:librte_crypto_null.a -l:librte_crypto_octeontx.a -l:librte_crypto_openssl.a -l:librte_crypto_scheduler.a -l:librte_crypto_virtio.a -l:librte_compress_octeontx.a -l:librte_compress_zlib.a -l:librte_regex_cn9k.a -l:librte_ml_cnxk.a -l:librte_vdpa_ifc.a -l:librte_vdpa_nfp.a -l:librte_vdpa_sfc.a -l:librte_event_cnxk.a -l:librte_event_dlb2.a -l:librte_event_dpaa.a -l:librte_event_dpaa2.a -l:librte_event_dsw.a -l:librte_event_opdl.a -l:librte_event_skeleton.a -l:librte_event_sw.a -l:librte_event_octeontx.a -l:librte_baseband_acc.a -l:librte_baseband_fpga_5gnr_fec.a -l:librte_baseband_fpga_lte_fec.a -l:librte_baseband_la12xx.a -l:librte_baseband_null.a -l:librte_baseband_turbo_sw.a -l:librte_node.a -l:librte_graph.a -l:librte_pipeline.a -l:librte_table.a -l:librte_pdump.a -l:librte_port.a -l:librte_fib.a -l:librte_pdcp.a -l:librte_ipsec.a -l:librte_vhost.a -l:librte_stack.a -l:librte_security.a -l:librte_sched.a -l:librte_reorder.a -l:librte_rib.a -l:librte_mldev.a -l:librte_regexdev.a -l:librte_rawdev.a -l:librte_power.a -l:librte_pcapng.a -l:librte_member.a -l:librte_lpm.a -l:librte_latencystats.a -l:librte_jobstats.a -l:librte_ip_frag.a -l:librte_gso.a -l:librte_gro.a -l:librte_gpudev.a -l:librte_dispatcher.a -l:librte_eventdev.a -l:librte_efd.a -l:librte_dmadev.a -l:librte_distributor.a -l:librte_cryptodev.a -l:librte_compressdev.a -l:librte_cfgfile.a -l:librte_bpf.a -l:librte_bitratestats.a -l:librte_bbdev.a -l:librte_acl.a -l:librte_timer.a -l:librte_hash.a -l:librte_metrics.a -l:librte_cmdline.a -l:librte_pci.a -l:librte_ethdev.a -l:librte_meter.a -l:librte_net.a -l:librte_mbuf.a -l:librte_mempool.a -l:librte_rcu.a -l:librte_ring.a -l:librte_eal.a -l:librte_telemetry.a -l:librte_kvargs.a -l:librte_log.a -Wl,--no-whole-archive -Wl,--export-dynamic
Cflags: -I${includedir}
[root@bogon /]# cat $PKG_CONFIG_PATH/libhs.pc
prefix=/usr/local
exec_prefix=/usr/local
libdir=/usr/local/lib64
includedir=/usr/local/include

Name: libhs
Description: Intel(R) Hyperscan Library
Version: 5.4.2
Libs: -L${libdir} -lhs
Libs.private:  -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc
Cflags: -I${includedir}/hs

        Name: 模块名
        Description: 模块描述,pkg-config –list-all命令输出的第二列。
        URL: 更为详细的模块描述信息的URL地址,可选。
        Version: 版本号。
        Requires: 该模块所依赖的其他模块,可以没有。
        Requires.private: 该模块所依赖的其他模块,且无需第三方关切,可选
        Conflicts: 可能冲突的模块,常用于版本冲突控制。比如,Conflicts: libdpdk< 1.9,表示和libdpdk模块的1.9以下的版本有冲突。
        Cflags: 重要,pkg-config –cflags输出的内容,头文件包含路径。
        Libs: 重要,pkg-config –libs输出的内容,库/依赖库的路径。
        Libs.private: 不需要第三方关切的依赖库。

        这里面,对构建编译make文件最有用的,就是libs(库目录),cflags(头文件目录)和已安装模块列表(通过这个可以找到所需库的名称,也就是下面命令中的${name}),这可以分别通过pkg-config的3个命令得到。

        ① pkg-config ${name} -cflags

[root@bogon /]# pkg-config libdpdk -cflags
-I/usr/local/include -include rte_config.h -march=native -mrtm 

         ② pkg-config ${name} -libs

[root@bogon /]# pkg-config libdpdk -libs
-Wl,--as-needed -L/usr/local/lib64 -lrte_node -lrte_graph -lrte_pipeline -lrte_table -lrte_pdump -lrte_port -lrte_fib -lrte_pdcp -lrte_ipsec -lrte_vhost -lrte_stack -lrte_security -lrte_sched -lrte_reorder -lrte_rib -lrte_mldev -lrte_regexdev -lrte_rawdev -lrte_power -lrte_pcapng -lrte_member -lrte_lpm -lrte_latencystats -lrte_jobstats -lrte_ip_frag -lrte_gso -lrte_gro -lrte_gpudev -lrte_dispatcher -lrte_eventdev -lrte_efd -lrte_dmadev -lrte_distributor -lrte_cryptodev -lrte_compressdev -lrte_cfgfile -lrte_bpf -lrte_bitratestats -lrte_bbdev -lrte_acl -lrte_timer -lrte_hash -lrte_metrics -lrte_cmdline -lrte_pci -lrte_ethdev -lrte_meter -lrte_net -lrte_mbuf -lrte_mempool -lrte_rcu -lrte_ring -lrte_eal -lrte_telemetry -lrte_kvargs -lrte_log 
[root@bogon /]# 

        ③ pkg-config --list-all

[root@bogon lib]# pkg-config --list-all
libdpdk-libs                   dpdk-libs - Internal-only DPDK pkgconfig file. Not for direct use.
libdpdk                        DPDK - The Data Plane Development Kit (DPDK).
libhs                          libhs - Intel(R) Hyperscan Library
fontutil                       FontUtil - Font utilities dirs
grilo-plugins-0.3              Grilo Framework Plugins - Plugins for the Grilo Framework
zlib                           zlib - zlib compression library
libzstd                        zstd - fast lossless compression algorithm library
libelf                         libelf - elfutils libelf library to read and write ELF files
liblzma                        liblzma - General purpose data compression library
……
……

         可以看到,pkg-config工具对我们构建make文件有极大帮助。可以使我们规避在make文件中写入与本机强相关的“硬路径”,在只需要调用pkg-config路径就可以得到模块安装路径的情况下,我们可以很便利的写出通用的make文件,从而在在任意路径上安装该模块的任意机器上运行。

        通过yum list查看pkg-config其是否安装,在CentOS上,它的名字是pkgconf:

[root@localhost yum.repos.d]# yum list pkgconf
上次元数据过期检查:0:08:12 前,执行于 2024年09月21日 星期六 09时13分32秒。
已安装的软件包
pkgconf.x86_64                                                                                        1.4.2-1.el8                                                                                         @anaconda

         参考:

         pkg-config 详解_$(pkg-config --cflags --libs pinocchio)-CSDN博客

(三)选择匹配的python3和pip3版本

        在CentOS Stream 8中(安装了Development Tools),已经自带了python3,但是默认的版本是3.6.8。使用meson进行编译时,会有一句告警称其需要python 3.7以上版本。虽然实际尝试过程中没有发现低版本引起什么问题,但毕竟警告了,升级一下也好。

        1. 检查并更新python版本

        检查python3版本:

[root@localhost yum.repos.d]# python3 --version
Python 3.6.8

        查看默认会发现系统中是有python 3.11的,只不过默认选择的是3.6

[root@bogon build]# ls -l /usr/bin/py*
lrwxrwxrwx. 1 root root   25 9月  16 17:49 /usr/bin/pydoc-3 -> /etc/alternatives/pydoc-3
lrwxrwxrwx. 1 root root   24 9月  16 17:49 /usr/bin/pydoc3 -> /etc/alternatives/pydoc3
-rwxr-xr-x. 1 root root   79 1月  26 2024 /usr/bin/pydoc3.11
-rwxr-xr-x. 1 root root   89 4月  24 17:38 /usr/bin/pydoc3.6
lrwxrwxrwx. 1 root root   25 9月  16 17:49 /usr/bin/python3 -> /etc/alternatives/python3
-rwxr-xr-x. 1 root root 7752 1月  26 2024 /usr/bin/python3.11
lrwxrwxrwx. 1 root root   31 11月 28 2023 /usr/bin/python3.6 -> /usr/libexec/platform-python3.6
lrwxrwxrwx. 1 root root   32 11月 28 2023 /usr/bin/python3.6m -> /usr/libexec/platform-python3.6m
lrwxrwxrwx. 1 root root   26 9月  16 17:49 /usr/bin/pyvenv-3 -> /etc/alternatives/pyvenv-3
-rwxr-xr-x. 1 root root  446 4月  24 17:38 /usr/bin/pyvenv-3.6

         使用update-alternatives指令更改默认版本

[root@bogon build]# update-alternatives --config python3

共有 2 个提供“python3”的程序。

  选项    命令
-----------------------------------------------
*+ 1           /usr/bin/python3.6
   2           /usr/bin/python3.11

按 Enter 保留当前选项[+],或者键入选项编号: 2
[root@bogon build]# 

         再看默认命令版本

[root@bogon build]# python3 --version
Python 3.11.7
[root@bogon build]# 

         如果没有python3.6以上版本,也可使用update-alternatives安装

update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11

        2. 检查并更新pip3版本        

        如果时如上所属更改了默认的python3版本,再运行pip3则可能出现找不到命令的问题

[root@bogon dpdk]# pip3 install meson
bash: pip3: 未找到命令...
提供此文件的软件包是:
'python3.11-pip'
'python3.12-pip'
'python36'
'python38-pip'
'python39-pip'

         需要重新去下载安装一下

[root@bogon opt]# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2213k  100 2213k    0     0  1936k      0  0:00:01  0:00:01 --:--:-- 1936k
[root@bogon opt]# ls
dpdk  get-pip.py

        执行一下,使用--user参数可以规避权限问题。这里我们是在root下执行的。用哪个版本的python执行get-pip.py,下载和关联的就是对应可用版本的pip。

[root@bogon opt]# python3 get-pip.py
Collecting pip
  Downloading pip-24.2-py3-none-any.whl.metadata (3.6 kB)
Collecting setuptools
  Downloading setuptools-75.1.0-py3-none-any.whl.metadata (6.9 kB)
Collecting wheel
  Downloading wheel-0.44.0-py3-none-any.whl.metadata (2.3 kB)
Downloading pip-24.2-py3-none-any.whl (1.8 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 5.8 MB/s eta 0:00:00
Downloading setuptools-75.1.0-py3-none-any.whl (1.2 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 11.2 MB/s eta 0:00:00
Downloading wheel-0.44.0-py3-none-any.whl (67 kB)
Installing collected packages: wheel, setuptools, pip
Successfully installed pip-24.2 setuptools-75.1.0 wheel-0.44.0
[root@bogon opt]# python3 get-pip.py --user
Collecting pip
  Using cached pip-24.2-py3-none-any.whl.metadata (3.6 kB)
Using cached pip-24.2-py3-none-any.whl (1.8 MB)
Installing collected packages: pip
  WARNING: The scripts pip, pip3 and pip3.11 are installed in '/root/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-24.2

         然后检查:

[root@bogon opt]# pip3 --version
pip 24.2 from /root/.local/lib/python3.11/site-packages/pip (python 3.11)

(四)安装meson 和 ninja

        1. meson

        Meson(The Meson Build System),开源的项目构建系统,类似于make。 不同的是Meson由 Python 实现,提供可定制语言(custom language)作为主要工具,用户可以使用它完成项目构建的描述,其设计与语法与 Python 语言形似。Meson 相比 make来说,不仅仅支持 C/C++,还支持多种编程语言。

        以项目目录结构如下为例:

├── meson.build
└── src
		├── include
		│   	└── mylib.h
		├── lib
		│		└── mylib.a
		└── main.c

        对应示例meson.build文件中包含了编译中常见的要素,包含文件目录,依赖库目录,主源代码文件和目标文件名称等:

project(			# 描述项目相关信息
	'Demo1', 		# 项目名称
	'c'				# 编程语言
) 
# 库文件
libs=meson.get_compiler('c').find_library(
                    'static_library',
                     dirs : join_paths(meson.source_root(),'src/lib'))    #拼接路径,指向meson.build文件所在目录的子目录./src/lib

executable(
    'uselib', 				# 目标程序文件名
    'src/main.c', 			# 源代码文件
    dependencies : libs, 	# 依赖项
    include_directories : 'src/include' # 头文件目录
)

         编译时,只需要在meson.build文件所在目录执行meson compile,或者可以使用meson -D<parameter> [build dir] [source dir] 执行带参数的配置编译。

        由于meson提供了诸多方便的命令,甚至可以直接依赖meson命令创建一个项目,避免了自行编辑一个meson.build文件:

# 创建一个新目录来保存项目文件
$ mkdir meson_project

# 进入项目目录
$ cd meson_project

# 使用Meson初始化并构建一个新的C/C++项目,会自动生成"meson.build"配置文件和C/C++源文件
$ meson init --name meson_project --build

# 项目构建完成后,默认的构建目录是build,可以直接运行构建生成的可执行文件
$ build/meson_project

# 进入build目录
$ cd build

# 重新构建代码
$ meson compile

        2. ninja

        ninja是一种构建工具,用来调用各种(代码生成器、编译器、链接器等)工具来编译大型项目,ninja的目的只是记录调用各种工具的命令,并且根据这些命令的输入输出关系来构建依赖图,所以它会把一些模糊复杂的操作交给元构建系统(meta-build system,例如cmake),只留下真正需要编译的命令。本质上,ninja在设计之初就不是给人写的,只是用于作为其它程序生成的目标。

        ninja通过分析一系列命令之间的依赖关系实现两个重要特点:

        ​并行编译:确定没有依赖的命令,并并行执行它们。

        增量编译:根据文件的时间戳进行分析,如果某个文件的时间戳发生了改变,则依赖于这个文件的命令以及其他依赖于这个命令的命令都会被重新执行,以此达到增量编译的效果。

        一个简单的build.ninja的例子

# build.ninja
cxxflags = -DBIT32

rule compile
    description = compile with $cxxflags
    command = g++ -MM -MF $out.d -MT $out $cxxflags $in && g++ $cxxflags $in -o $out
    depfile = $out.d

build compile_with_default_cxxflags: compile main.cpp

build compile_with_shadow_cxxflags: compile main.cpp
    cxxflags = -DBIT64

default compile_with_default_cxxflags

        注释(comment): build.ninja里用#开头的行表示注释,这个没什么好解释的。
        变量(variable): cxxflags = -DUSE_A1定义了一个变量cxxflags,其值为-DBIT32。这个带入到gcc/g++编译器的参数中,即为配置参数,参数值USE_A1,等同于在代码中#define BIT32。这样配合后面build命令,及第二条build命令中被局部变量覆盖的同名变量值,可以看出两次编译使用了不同的#define参数,一次为BIT32(比如用此标记32位相关代码),另一次为BIT64。变量的引用方式为$cxxflags。
        规则(rule): 语法为rule 加上规则的名字,这里是名为compile的规则。规则内有默认变量$in和$out,$in表示输入文件的列表,$out表示输出文件名。这个$in ,$out和后面的“build”决定了ninja对依赖图的分析构建。
        规则的文字描述(description):用于在编译过程中展示并告诉用户目前在干什么。
        规则的具体命令(command): 写明具体的编译命令,其实就是调用其它工具的命令
        规则的依赖项记录文件(depfile): 用于增量编译。
        构建(build): 语法为build加上构建的文件名,加上冒号:以及规则名、输入文件列表。即 “build $out: $rule $in”。这个句式的意思就是把$in和$out传给$rule,然后运行它的command命令。
        构建里的局部变量: 即在build下面的任意变量,会用来覆盖同名全局的变量。
        默认构建目标(default):可以存在多行default,最后的默认构建目标就是它们的并集。如果没有任何default,则默认构建全部的build条目。

        3. 安装meson、ninja和pyelftools

        这3个工具一定要在同一个版本的pip3版本下面安装,否则会互相找不到而导致编译报错。

[root@localhost yum.repos.d]# pip3 install meson ninja
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting meson
  Downloading https://files.pythonhosted.org/packages/18/db/3feb3cfa102553b9329d0c887b3c10480381de69abf6e8629f6b32f450df/meson-0.61.5-py3-none-any.whl (862kB)
    100% |████████████████████████████████| 870kB 2.2MB/s 
Collecting ninja
  Downloading https://files.pythonhosted.org/packages/6d/92/8d7aebd4430ab5ff65df2bfee6d5745f95c004284db2d8ca76dcbfd9de47/ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl (307kB)
    100% |████████████████████████████████| 307kB 4.3MB/s 
Installing collected packages: meson, ninja
Successfully installed meson-0.61.5 ninja-1.11.1.1

        pyelftools:

[root@localhost yum.repos.d]# pip3 install pyelftools
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting pyelftools
  Downloading https://files.pythonhosted.org/packages/f8/64/711030d9fe9ccaf6ee3ab1bcf4801c6bb3d0e585af18824a50b016b4f39c/pyelftools-0.31-py3-none-any.whl (180kB)
    100% |████████████████████████████████| 184kB 1.7MB/s 
Installing collected packages: pyelftools
Successfully installed pyelftools-0.31

         参考:

        Meson构建系统的使用_meson.build-CSDN博客

        Meson 入门指南之一 - RioTian - 博客园

        一文读懂ninja构建系统 - 知乎 (zhihu.com)

        Meson构建系统的使用_meson.build-CSDN博客

(五)其它选装项

        除了必须的以外,还有一些选装的,如果缺失,DPDK会检测并且跳过对应需要这些库的模块,比如:

        1. 安装numactl开发包

[root@localhost yum.repos.d]# yum install numactl-devel.x86_64 -y
上次元数据过期检查:0:37:40 前,执行于 2024年09月21日 星期六 09时13分32秒。
依赖关系解决。
===================================================================================================================================================================================================================
 软件包                                                架构                                           版本                                                    仓库                                            大小
===================================================================================================================================================================================================================
安装:
 numactl-devel                                         x86_64                                         2.0.16-4.el8                                            baseos                                          30 k
……
……   

已安装:
  numactl-devel-2.0.16-4.el8.x86_64                                                                                                                                                                                

完毕!

        2. libarchive

[root@bogon usertools]# yum list libarchive*
上次元数据过期检查:0:51:43 前,执行于 2024年09月21日 星期六 23时01分09秒。
已安装的软件包
libarchive.x86_64                                                                                         3.3.3-5.el8                                                                                    @baseos   
libarchive-devel.x86_64                                                                                   3.3.3-5.el8                                                                                    @appstream

        3. elfutils-libelf,elfutils-libelf-devel

[root@bogon usertools]# yum list elfutils-libelf*
上次元数据过期检查:0:52:44 前,执行于 2024年09月21日 星期六 23时01分09秒。
已安装的软件包
elfutils-libelf.x86_64                                                                                        0.190-2.el8                                                                                   @baseos
elfutils-libelf-devel.x86_64                                                                                  0.190-2.el8                                                                                   @baseos

       不装有啥问题不知道,反正装了也不麻烦。

         参考:

 2. System Requirements — Data Plane Development Kit 24.07.0 documentation (dpdk.org)

三、编译DPDK

(一)准备工作

        1. 下载

        这个比较简单,到DPDK官网的download下下载就行了,最好是下载长期稳定版本(LTS)。可以curl,可以wget,也可以在windows上下了压缩包拷贝到CentOS上。

        2. 解压

        我是按照自己的习惯将DPDK解压到了/opt目录下,其它地方也可以,看个人喜好了。

[root@localhost opt]# tar vxf dpdk.tar.xz 
dpdk-stable-23.11.2/
dpdk-stable-23.11.2/.ci/
dpdk-stable-23.11.2/.ci/linux-build.sh
dpdk-stable-23.11.2/.ci/linux-setup.sh
dpdk-stable-23.11.2/.editorconfig
dpdk-stable-23.11.2/.gitattributes
dpdk-stable-23.11.2/.github/
dpdk-stable-23.11.2/.github/workflows/
……
……

        名字改简单点,方便后面经常访问的时候少敲点键盘;

        压缩包用完就删了,可以节约点空间,玩docker后就有这种强迫症。

[root@localhost opt]# mv dpdk-stable-23.11.2/ dpdk
[root@localhost opt]# ls
dpdk  dpdk.tar.xz
[root@localhost opt]# rm -f dpdk.tar.xz 

(二) 编译

        1. 确定编译选项

根据官方的指南以及网上的一些其它指南,在编译时可以通过-D参数确定一些编译选项,比如:

        -Dbuildtype=debugoptimized

        -Dplatform=native

        这个platform参数定义为native,等同于设置了

        cpu_instruction_set=native
        -march(x86_64)=native
        -mcpu(ppc)=native
        -mtune(ppc)=native

        这么一连串。这里面,指令集可以使用 cpu_instruction_set定义,取值可以是corei7,power8之类。

        逻辑核数量使用 max_lcores定义,其值会在DPDK程序中以RTE_MAX_LCORE宏的形式出现;注意这个值的定义:由于程序中我们经常需要轮询逻辑核,或者以逻辑核的数量为基准来定义一些数据结构数组,这个数值定义过大显然是不合适的。

        这些参数可以带入编译命令,也可以在编译后再配置

        ① 编译时 -Dmax_lcores=256

        ② 编译后 meson configure -Dmax_lcores=256

        2. 构建

        简单起见,我们在构建中没有过多设置配置参数,但通过-Dexamples可以设置编译DPDK的examples中的哪几个。作为测试学习而言,用-Dexamples=all也无可厚非。

        meson setup -Dexamples=l2fwd,l3fwd,flow_filtering build

[root@localhost dpdk]# meson setup -Dexamples=l2fwd,l3fwd,flow_filting build
The Meson build system
Version: 0.61.5
Source dir: /opt/dpdk
Build dir: /opt/dpdk/build
Build type: native build
Program cat found: YES (/usr/bin/cat)
Project name: DPDK
Project version: 23.11.2
C compiler for the host machine: cc (gcc 8.5.0 "cc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-22)")
C linker for the host machine: cc ld.bfd 2.30-123
Host machine cpu family: x86_64
Host machine cpu: x86_64
Program pkg-config found: YES (/usr/bin/pkg-config)
Program check-symbols.sh found: YES (/opt/dpdk/buildtools/check-symbols.sh)
Program options-ibverbs-static.sh found: YES (/opt/dpdk/buildtools/options-ibverbs-static.sh)
Program python3 (elftools) found: YES (/usr/bin/python3) modules: elftools
Program cat found: YES (/usr/bin/cat)
Compiler for C supports arguments -march=native: YES 
Checking for size of "void *" : 8
Checking for size of "void *" : 8
Compiler for C supports link arguments -Wl,--undefined-version: NO 
Library m found: YES
Library numa found: YES
……
……
Compiler for C supports arguments -Wno-format-truncation: YES (cached)
Configuring rte_build_config.h using configuration
Message: 
=================
Applications Enabled
=================

apps:
	graph, pdump, proc-info, test-acl, test-bbdev, test-cmdline, test-compress-perf, test-crypto-perf, 
	test-dma-perf, test-eventdev, test-fib, test-flow-perf, test-gpudev, test-mldev, test-pipeline, test-pmd, 
	test-regex, test-sad, test-security-perf, test, 

Message: 
=================
Libraries Enabled
=================

libs:
	log, kvargs, telemetry, eal, ring, rcu, mempool, mbuf, 
	net, meter, ethdev, pci, cmdline, metrics, hash, timer, 
	acl, bbdev, bitratestats, bpf, cfgfile, compressdev, cryptodev, distributor, 
	dmadev, efd, eventdev, dispatcher, gpudev, gro, gso, ip_frag, 
	jobstats, latencystats, lpm, member, pcapng, power, rawdev, regexdev, 
	mldev, rib, reorder, sched, security, stack, vhost, ipsec, 
	pdcp, fib, port, pdump, table, pipeline, graph, node, 
	

Message: 
===============
Drivers Enabled
===============

common:
	cpt, dpaax, iavf, idpf, octeontx, cnxk, nfp, qat, 
	sfc_efx, 
bus:
	auxiliary, cdx, dpaa, fslmc, ifpga, pci, platform, vdev, 
	vmbus, 
mempool:
	bucket, cnxk, dpaa, dpaa2, octeontx, ring, stack, 
dma:
	cnxk, dpaa, dpaa2, hisilicon, idxd, ioat, skeleton, 
net:
	af_packet, ark, atlantic, avp, axgbe, bnx2x, bnxt, bond, 
	cnxk, cpfl, cxgbe, dpaa, dpaa2, e1000, ena, enetc, 
	enetfec, enic, failsafe, fm10k, gve, hinic, hns3, i40e, 
	iavf, ice, idpf, igc, ionic, ixgbe, memif, netvsc, 
	nfp, ngbe, null, octeontx, octeon_ep, pfe, qede, ring, 
	softnic, tap, thunderx, txgbe, vdev_netvsc, vhost, virtio, vmxnet3, 
	
raw:
	cnxk_bphy, cnxk_gpio, dpaa2_cmdif, ntb, skeleton, 
crypto:
	bcmfs, caam_jr, ccp, cnxk, dpaa_sec, dpaa2_sec, nitrox, null, 
	octeontx, openssl, scheduler, virtio, 
compress:
	octeontx, zlib, 
regex:
	cn9k, 
ml:
	cnxk, 
vdpa:
	ifc, nfp, sfc, 
event:
	cnxk, dlb2, dpaa, dpaa2, dsw, opdl, skeleton, sw, 
	octeontx, 
baseband:
	acc, fpga_5gnr_fec, fpga_lte_fec, la12xx, null, turbo_sw, 
gpu:
	

Message: 
=================
Content Skipped
=================

apps:
	dumpcap:	missing dependency, "libpcap"
	
libs:
	
drivers:
	common/mvep:	missing dependency, "libmusdk"
	common/mlx5:	missing dependency, "mlx5"
	net/af_xdp:	missing dependency, "libxdp >=1.2.2" and "libbpf"
	net/ipn3ke:	missing dependency, "libfdt"
	net/mana:	missing dependency, "ibverbs"
	net/mlx4:	missing dependency, "mlx4"
	net/mlx5:	missing internal dependency, "common_mlx5"
	net/mvneta:	missing dependency, "libmusdk"
	net/mvpp2:	missing dependency, "libmusdk"
	net/nfb:	missing dependency, "libnfb"
	net/pcap:	missing dependency, "libpcap"
	net/sfc:	broken dependency, "libatomic"
	raw/ifpga:	missing dependency, "libfdt"
	crypto/armv8:	missing dependency, "libAArch64crypto"
	crypto/ipsec_mb:	missing dependency, "libIPSec_MB"
	crypto/mlx5:	missing internal dependency, "common_mlx5"
	crypto/mvsam:	missing dependency, "libmusdk"
	crypto/uadk:	missing dependency, "libwd"
	compress/isal:	missing dependency, "libisal"
	compress/mlx5:	missing internal dependency, "common_mlx5"
	regex/mlx5:	missing internal dependency, "common_mlx5"
	vdpa/mlx5:	missing internal dependency, "common_mlx5"
	gpu/cuda:	missing dependency, "cuda.h"
	

Build targets in project: 643

DPDK 23.11.2

  User defined options
    examples: l2fwd,l3fwd,flow_filtering

Found ninja-1.11.1.git.kitware.jobserver-1 at /usr/local/bin/ninja

        3. 补充开发库依赖

       查看构建结果,虽然返回的情况是成功了,但也缺失了部分库。其中一些是特定的网卡驱动库。其它的在后面暂时没发现用上过,似乎缺失问题也不大。不过,缺别的啥都可以忍,libpcap有点忍不了,添上再说:

        libpcap-devel模块在PowerTools repo库里面,需要通过--enablerepo参数显示指示:

[root@localhost build]# yum --enablerepo=powertools install libpcap-devel

         这时不能再次编译了,直接reconfigure就可以

[root@localhost dpdk]# meson setup --reconfigure build

        其它的部分如果实在想装,可以尝试使用如下名称安装:

yum install gcc-gfortran kernel-modules-extra tcl tk tcsh terminator tmux kernel-rpm-macros elfutils-libelf-devel libnl3-devel

        或者如果解析不到,可以直接到官网上去找rpm包,如libnl3-devel,不赘述:

https://rpmfind.net/linux/opensuse/tumbleweed/repo/oss/x86_64/libnl3-devel-3.10.0-1.1.x86_64.rpm

        4. 编译

        构建结束后,在dpdk下会生成build目录,其中build.ninja文件应该已经准备好了,在该目录下直接执行ninja即可。

[root@bogon ~]# cd /opt/dpdk/build
[root@bogon build]# ninja
[2512/2512] Linking target app/dpdk-test
[root@bogon build]# 

         5. 安装

        编译完成后,使用meson install进行安装,基本上就是把一些代码和库搬了个地方:

[root@bogon build]# meson install
ninja: Entering directory `/opt/dpdk/build'
ninja: no work to do.
Installing subdir /opt/dpdk/examples to /usr/local/share/dpdk/examples
Installing /opt/dpdk/examples/bbdev_app/Makefile to /usr/local/share/dpdk/examples/bbdev_app
Installing /opt/dpdk/examples/bbdev_app/main.c to /usr/local/share/dpdk/examples/bbdev_app
Installing /opt/dpdk/examples/bond/Makefile to /usr/local/share/dpdk/examples/bond
Installing /opt/dpdk/examples/bond/commands.list to /usr/local/share/dpdk/examples/bond
Installing /opt/dpdk/examples/bond/main.c to /usr/local/share/dpdk/examples/bond
Installing /opt/dpdk/examples/bpf/README to /usr/local/share/dpdk/examples/bpf
Installing /opt/dpdk/examples/bpf/dummy.c to /usr/local/share/dpdk/examples/bpf
Installing /opt/dpdk/examples/bpf/t1.c to /usr/local/share/dpdk/examples/bpf
……
……

四、测试DPDK

        前文在介绍预备知识的时候,铺垫了诸如大页内存设置、网卡驱动绑定之类的要求。事实上,在搭建环境的时候,这些事并没有在测试DPDK示例程序之前就做好,而是在运行示例程序不断的“状况百出”中“查缺补漏”的,感觉理解会更深一些:

        (一)Helloworld和大页内存配置

        Helloworld用例是DPDK诸多examples中最简单的一个。从源代码看,其主要就是调用了rte_eal_init函数初始化EAL层,这会涉及到逻辑核的分配(也就是-c -l 和 --lcores 这些EAL通用参数的解析及执行),内存的分配和配置(即 -n -r这些EAL通用参数的配置和执行);

static int lcore_hello(__rte_unused void *arg)
{
        unsigned lcore_id;
        lcore_id = rte_lcore_id();
        printf("hello from core %u\n", lcore_id);
        return 0;
}

main(int argc, char **argv)
{
        int ret;
        unsigned lcore_id;

        //初始化逻辑核、内存等EAL资源
        ret = rte_eal_init(argc, argv);
        if (ret < 0)rte_panic("Cannot init EAL\n");

        //枚举工作线程,调用lcore_hello
        RTE_LCORE_FOREACH_WORKER(lcore_id) {
                rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
        }
        //在主线程里面也调用lcore_hello
        lcore_hello(NULL);

        //等待所有线程结束做资源清理
        rte_eal_mp_wait_lcore();
        rte_eal_cleanup();
        return 0;
}

        所以,只要Helloworld能执行下去,至少说明CPU和内存的配置方面没有什么大的问题。

        好了,假设我们前面什么配置都没做,上来先试一下:

[root@bogon examples]# ./dpdk-helloworld
EAL: Detected CPU lcores: 16
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: No free 2048 kB hugepages reported on node 0
EAL: FATAL: Cannot get hugepage information.
EAL: Cannot get hugepage information.
PANIC in main():
Cannot init EAL
0: ./dpdk-helloworld (rte_dump_stack+0x2a) [90641a]
1: ./dpdk-helloworld (__rte_panic+0xba) [4ed0fe]
2: ./dpdk-helloworld (400000+0xcc2d6) [4cc2d6]
3: /lib64/libc.so.6 (__libc_start_main+0xe5) [7f12bb7057e5]
4: ./dpdk-helloworld (_start+0x2e) [6e427e]
已放弃 (核心已转储)
[root@bogon examples]# 

        看报错,是缺少大页内存,那么按照如前所述的方法设置大页内存。简单起见,我们使用动态方法,并且使用系统默认的大页大小。这样,就不用去修改GRUB,也不用mount。

[root@bogon usertools]# echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
[root@bogon usertools]# echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
[root@bogon usertools]# 

        此时通过/proc/meminfo查看,可以看到hugepage已经按照我们设置的数量配置好了:

[root@bogon usertools]# cat /proc/meminfo
MemTotal:        7849632 kB
MemFree:         1464368 kB
MemAvailable:    3780764 kB
Buffers:            4400 kB
Cached:          2470484 kB
SwapCached:            0 kB
Active:          1198964 kB
Inactive:        2320656 kB
……  ……
ShmemPmdMapped:        0 kB
FileHugePages:         0 kB
FilePmdMapped:         0 kB
HugePages_Total:    1024
HugePages_Free:     1024
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
…… ……

        使用 cat /proc/mounts |grep "huge"查看mount情况,可以看到大页内存被挂载到系统默认挂载点/dev/hugepages上:

[root@bogon usertools]# cat /proc/mounts | grep "huge"
cgroup /sys/fs/cgroup/hugetlb cgroup rw,seclabel,nosuid,nodev,noexec,relatime,hugetlb 0 0
hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime,pagesize=2M 0 0

         当然,使用工具直接看更清楚:

[root@bogon dpdk]# usertools/dpdk-hugepages.py --show
Node Pages Size Total
0    1024  2Mb    2Gb
Hugepages mounted on /dev/hugepages

        再重新测试一下:

        可以看到,现在内存分配没啥问题了。至于CPU,由于我们没有设置-c -l等参数,EAL自行检测了所有的逻辑核,然后按照程序执行流程,在所有非主逻辑核上启动了工作线程(1-15),然后在主线程中也启动了一个工作线程(0)。

[root@bogon examples]# ./dpdk-helloworld
EAL: Detected CPU lcores: 16
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
TELEMETRY: No legacy callbacks, legacy socket not created
hello from core 1
hello from core 2
hello from core 3
hello from core 4
hello from core 5
hello from core 6
hello from core 7
hello from core 8
hello from core 9
hello from core 10
hello from core 11
hello from core 12
hello from core 13
hello from core 14
hello from core 15
hello from core 0

        (二)l2fwd和网卡绑定

        l2fwd是一个二层转发的示例,主要代码就不能像helloworld那样直接贴上来了——太长太复杂。为了实现l2fwd转发,该程序的一个重要工作是初始化NIC(网络接口卡,对应在程序里叫port),并且它要求收发端口成对出现。

struct port_pair_params {
#define NUM_PORTS       2
        uint16_t port[NUM_PORTS];
} __rte_cache_aligned;

 然后在main函数中构建收发端口对应关系,保存在数据结构中以备后用,以形成相邻端口(未被占用情况下 )互为收发的初衷。

        /* populate destination port details */
        if (port_pair_params != NULL) {
                uint16_t idx, p;

                for (idx = 0; idx < (nb_port_pair_params << 1); idx++) {
                        p = idx & 1;
                        portid = port_pair_params[idx >> 1].port[p];
                        l2fwd_dst_ports[portid] =
                                port_pair_params[idx >> 1].port[p ^ 1];
                }
        } else {
                RTE_ETH_FOREACH_DEV(portid) {
                        /* skip ports that are not enabled */
                        if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
                                continue;

                        if (nb_ports_in_mask % 2) {
                                l2fwd_dst_ports[portid] = last_port;
                                l2fwd_dst_ports[last_port] = portid;
                        } else {
                                last_port = portid;
                        }

                        nb_ports_in_mask++;
                }
                if (nb_ports_in_mask % 2) {
                        printf("Notice: odd number of ports in portmask.\n");
                        l2fwd_dst_ports[last_port] = last_port;
                }
        }

        这里我们不打算深入l2fwd的原理,因为其中还包括了大量的参数解析、防网卡不成对、端口被占用容错相关的代码,对初学不十分友好(个人感觉初学最好商定好环境,代码容错率低些,这样至少主干逻辑会非常清楚)。如果需要了解具体原理,在DPDK的官网上就有比较详细的介绍:16. L2 转发示例应用程序(在真实和虚拟化环境中)— 数据平面开发工具包 24.11.0-rc0 文档 (dpdk.org)        我们只需要知道一条:它涉及到网络接口卡的初始化。所以,只要网络接口卡配置不对,这个l2fwd一定执行不了:

[root@bogon examples]# ./dpdk-l2fwd 
EAL: Detected CPU lcores: 16
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
TELEMETRY: No legacy callbacks, legacy socket not created
MAC updating enabled
EAL: Error - exiting with code: 1
  Cause: No Ethernet ports - bye

        如图上所说,No Ethernet Ports 拜拜。意思就是,没有网卡;当然l2fwd也不是必须两块网卡才能运转,1块也行。而且,我们也并不是没有网卡,只不过是这块网卡所用的kernel driver是vmnet3,不是支持DPDK的VFIO罢了。

[root@bogon testdpdkfilter]# lspci |grep Ethernet
03:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)

[root@bogon testdpdkfilter]# lspci -vvv -s 03:00.0
03:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)
	DeviceName: Ethernet0
	Subsystem: VMware VMXNET3 Ethernet Controller
……
……
	Kernel driver in use: vmxnet3
	Kernel modules: vmxnet3

        1. 在虚拟机中增加网卡

        虽然直接使用虚拟机中原有的那个虚拟网卡也无不可,但为了后面测试DPDK变成方便(我们计划基于DPDK构建一个在虚拟机上监听本机网络通讯的程序,所以除了本机用来上网的网卡外,还需要有一个能够提供DPDK使用的网卡。同样的原因,我们希望添加的网卡和原虚拟机的网卡连接在同一个虚拟交换机上,而不是和宿主机网卡连接在一个交换机上,所以选择两个网卡都采用NAT连接方式,这一点与网络上一些教程(一块使用NAT,一块使用桥接)不太一致。

        也有网上教程提及需要更改虚拟机配置文件vmx(虚拟机构建时创建的那个,并且还会询问用户该文件的放置目录),在其中加入ehternet1.wakeOnPcktRcv = "TRUE"这么一行,实际测试中未发现该操作有和影响:

 网卡的配置

        2. 查看网卡驱动绑定状态

        尽管增加了一块网卡,但在没有为网卡安装VFIO驱动的情况下,DPDK仍然是找不到的。可以使用DPDK提供的用户工具dpdk-devbind.py工具来查看网卡驱动的绑定状态:

[root@bogon usertools]# ./dpdk-devbind.py --status

Network devices using kernel driver
===================================
0000:03:00.0 'VMXNET3 Ethernet Controller 07b0' if=ens160 drv=vmxnet3 unused= *Active*
0000:13:00.0 'VMXNET3 Ethernet Controller 07b0' if=ens224 drv=vmxnet3 unused= 

         增加网卡后,可以看到在PCI 13:00.0上有一块新的网卡,驱动与原网卡一样,都是vmxnet3,未激活(即不在启用状态,对应使用ifconfig up/down切换的状态)。

        3. 使能IOMMU,安装VFIO

        如果在绑定VFIO的过程中出现错误

[root@bogon usertools]# modprobe vfio-pci

[root@bogon usertools]# ./dpdk-devbind.py --bind vfio-pci 13:00.0
Warning: no supported DPDK kernel modules are loaded
Error: Driver 'vfio-pci' is not loaded.

        那就是说VFIO驱动尚未安装。如上文所述,需要是能IOMMU(VFIO需要),然后安装VFIO驱动:

        (1) 修改开机启动项,使能IOMMU

        在/etc/default/grub中,GRUB_COMLINE_LINUX这一行,后面加上:

        iommu=pt intel_iommu=on

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap rhgb quiet iommu=pt intel_iommu=on"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true

        生成启动配置文件

[root@bogon build]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
done
[root@bogon build]reboot

        重新启动后,如果配置成功,可以在/proc/cmdline中查看到我们对应的改动:

[root@bogon ~]# cat /proc/cmdline |grep intel_iommu
BOOT_IMAGE=(hd0,msdos1)/vmlinuz-4.18.0-553.6.1.el8.x86_64 root=/dev/mapper/cs-root ro crashkernel=auto resume=/dev/mapper/cs-swap rd.lvm.lv=cs/root rd.lvm.lv=cs/swap rhgb quiet iommu=pt intel_iommu=on
[root@bogon ~]# 
        (2) 加载VFIO驱动
[root@bogon ~]# modprobe vfio
[root@bogon ~]# modprobe vfio-pci

[root@bogon ~]# lsmod|grep vfio_pci
vfio_pci               61440  0
vfio_virqfd            16384  1 vfio_pci
irqbypass              16384  1 vfio_pci
vfio                   36864  2 vfio_iommu_type1,vfio_pci
[root@bogon ~]# 

        加载成功后,使用lsmod可以查看到。

        (3) 使能unsafe_noiommu_mode

        完成驱动安装后,还需要将/sys/module/vfio/parameters下的enable_unsafe_noiommu_mode文件置为1。该项每次重启系统后都会还原为0,需要在启动项中设置,比如在~/.bashrc中。

[root@bogon build]# echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode

        如果没打开该选项,绑卡时会出现参数错误的报错类型:

[root@bogon usertools]# ./dpdk-devbind.py --bind vfio-pci 0000:13:00.0
Error: bind failed for 0000:13:00.0 - Cannot bind to driver vfio-pci: [Errno 22] Invalid argument

         官方的说明是:建议在所有情况下将 vfio-pci 用作 DPDK 绑定端口的内核模块。如果 IOMMU 不可用,则可以在无 IOMMU 模式下使用 vfio-pci。所以,如果此处非要打开无IOMMU模式才能绑定成功,难道是启动过程中使能iommu的过程失败了?这里没有再继续深究。

        (4) 绑定网卡和VFIO驱动

        万事俱备后,再去绑就绑上了,使用dpdk-devbind.py --status查看绑定状态,可以发现我们新增加的网卡(PCIe 13:00.0,其驱动已经变成了vfio-pci;vmxnet3的状态转为未使用。

[root@bogon usertools]# ./dpdk-devbind.py --bind vfio-pci 0000:13:00.0
[root@bogon usertools]# ./dpdk-devbind.py --status

Network devices using DPDK-compatible driver
============================================
0000:13:00.0 'VMXNET3 Ethernet Controller 07b0' drv=vfio-pci unused=vmxnet3

Network devices using kernel driver
===================================
0000:03:00.0 'VMXNET3 Ethernet Controller 07b0' if=ens160 drv=vmxnet3 unused=vfio-pci *Active*

       绑定VFIO前,使用ifconfig,是能够看到增加的网卡的;绑定VFIO后,再使用ifconfig,新增加的网卡就看不到了。此时,仅能够通过lspci或者dpdk-devbind.py --status查看。

使用l2fwd:

        (5)执行l2fwcd

        不带任何参数执行,这时候发现已经可以识别网卡了

[root@bogon examples]# ./dpdk-l2fwd
EAL: Detected CPU lcores: 16
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'PA'
EAL: VFIO support initialized
EAL: Using IOMMU type 8 (No-IOMMU)
EAL: Ignore mapping IO port bar(3)
EAL: Probe PCI driver: net_vmxnet3 (15ad:07b0) device: 0000:13:00.0 (socket -1)
TELEMETRY: No legacy callbacks, legacy socket not created
MAC updating enabled
Skipping disabled port 0
EAL: Error - exiting with code: 1
  Cause: All available ports are disabled. Please set portmask.

        但是口还不对,所有的口都不能用,要求我们设置端口掩码。

        如前文所述,端口掩码是l2fwd示例程序自己的命令行参数-p确定的。由于我们仅仅设置了1块网卡,也就只有1个端口,所以命令行参数应该为 -- -p 1

[root@bogon examples]# ./dpdk-l2fwd -- -p 1

         直接执行,可以看到,这一次成功了,程序会刷新成监控界面模式:


Port statistics ====================================
Statistics for port 0 ------------------------------
Packets sent:                       68
Packets received:                   68
Packets dropped:                     0
Aggregate statistics ===============================
Total packets sent:                 68
Total packets received:             68
Total packets dropped:               0
====================================================

        使用Ctrl + C退出程序。

五、HyperScan安装

        HyperScan是 Intel 提供的高性能正则表达式匹配库,可在 x86 平台上运行,并支持 Perl 兼容正则表达式 (PCRE) 语法、正则表达式组的同时匹配和流式处理操作。它在 BSD 许可证下作为开源软件发布。Hyperscan 提供了一个灵活的 C API 和多种不同的操作模式,以确保其在实际网络场景中的适用性。此外,Hyperscan 专注于高效算法和基于英特尔SIMD指令集的优化,使 Hyperscan 能够实现高匹配性能。它适用于深度包检测 (DPI)、入侵检测系统 (IDS)、入侵防御系统 (IPS) 和防火墙等使用场景,并已部署在全球网络安全解决方案中。Hyperscan 还已集成到广泛使用的开源 IDS 和 IPS 产品中,如 Snort 和 Suricata。

​        HyperScan官网:Hyperscan 简介 (intel.com)

        代码已经迁移到github上:GitHub - intel/hyperscan:高性能正则表达式匹配库

        Intel提供了DPDK+HyperScan的集成解决方案,可以支持高速网络数据包处理和转发,在网络行业中得到广泛应用,如用于实现高性能DPI解决方案等。下图是Intel官方给出的测试数据,使用真实模式和 HTTP 流量作为输入,在较大的数据包大小下,性能可以达到线速。


Hyperscan 和数据平面开发套件集成的性能

(一)Ragel安装

        Hyperscan基于Ragel状态机引擎实现,所以在安装Hyperscan之前,首先需要安装Ragel。Ragel官网:Ragel State Machine Compiler,软件包下载地址:

        Ragel State Machine Compiler

        1. 下载

[root@bogon opt]# curl https://www.colm.net/files/ragel/ragel-6.10.tar.gz --output ragel.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1204k  100 1204k    0     0  1990k      0 --:--:-- --:--:-- --:--:-- 1990k

       2.  解压

[root@bogon opt]# tar zxvf ragel.tar.gz
ragel-6.10/
ragel-6.10/configure.ac
ragel-6.10/AUTHORS
ragel-6.10/test/
…… ……

[root@bogon opt]# ls
dpdk  get-pip.py  ragel-6.10  ragel.tar.gz
[root@bogon opt]# mv ragel-6.10/ ragel
[root@bogon opt]# ls
dpdk  get-pip.py  ragel  ragel.tar.gz

         3. 配置

[root@bogon ragel]# ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
…… ……
…… ……
config.status: creating test/Makefile
config.status: creating test/runtests
config.status: creating examples/Makefile
config.status: creating ragel/config.h
config.status: executing depfiles commands
config.status: executing default commands
configuration of ragel complete

        4. 编译

        据说坑很多……但在我们的环境中还是有惊无险的结束了 

        5. 安装

[root@bogon ragel]# make install
Making install in ragel
make[1]: 进入目录“/opt/ragel/ragel”
make  install-am
make[2]: 进入目录“/opt/ragel/ragel”
make[3]: 进入目录“/opt/ragel/ragel”
…… ……
…… ……
make[1]: 进入目录“/opt/ragel”
make[2]: 进入目录“/opt/ragel”
make[2]: 对“install-exec-am”无需做任何事。
 /usr/bin/mkdir -p '/usr/local/share/doc/ragel'
 /usr/bin/install -c -m 644 CREDITS ChangeLog '/usr/local/share/doc/ragel'
make[2]: 离开目录“/opt/ragel”
make[1]: 离开目录“/opt/ragel”

        6. 刷新动态库

        最后需要在ragel目录下执行ldconfig,刷新动态库。

[root@bogon ragel]# ldconfig

        注:这一步可能并不需要,安装结束后ragel可以运行,且本地以及/usr/lib、/lib下均找不到与ragel相关的库。

        7. 检查

        返回版本号,应该算成功了

[root@bogon ragel]# ragel --version
Ragel State Machine Compiler version 6.10 March 2017
Copyright (c) 2001-2009 by Adrian Thurston
[root@bogon ragel]# 

(二)Boost Headers安装

        官方的帮助说:HyperScan的编译需要Boost C++ header库的支持,如果Boost库已经安装,则CMake可以自动发现并使用它们;如果没有安装,可以使用BOOST_ROOT参数在CMake的配置步骤中指明Boost源代码的路径;或者,一个可选的办法是利用软连接,将boost放在hyperscan的include下就可以:

        例如:对于Boost-1.59.0 release:

        ln -s boost_1_59_0/boost <hyperscan-source-path>/include/boost

        因为Hyperscan其实仅仅使用了Boost的头文件,所以可以考虑不完整编译安装Boost。

        当然,其实安装一下也不麻烦:

        1. 下载

        官方:https://sourceforge.net/projects/boost/files/boost/1.80.0/

       URL:https://jaist.dl.sourceforge.net/project/boost/boost/1.80.0/boost_1_80_0.tar.gz?viasf=1

[root@bogon opt]# curl https://jaist.dl.sourceforge.net/project/boost/boost/1.80.0/boost_1_80_0.tar.gz?viasf=1 --output boost.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 27  130M   27 36.2M    0     0  38703      0  0:58:51  0:16:20  0:42:31 39857
curl: (18) transfer closed with 98710292 bytes remaining to read
[root@bogon opt]#  

        2. 解压

        tar zxvf boost_1_86_0.tar.gz

        很大很长,就不截图了

        3. 编译B2引擎

        执行bootstrap.sh

[root@bogon boost]# ./bootstrap.sh 
Building B2 engine..

###
###
### Using 'gcc' toolset.
###
###

g++ (GCC) 8.5.0 20210514 (Red Hat 8.5.0-22)
Copyright © 2018 Free Software Foundation, Inc.
本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;
包括没有适销性和某一专用目的下的适用性担保。

###
###

> g++ -x c++ -std=c++11 -pthread -O2 -s -DNDEBUG bindjam.cpp builtins.cpp class.cpp command.cpp compile.cpp constants.cpp cwd.cpp debug.cpp debugger.cpp events.cpp execcmd.cpp execnt.cpp execunix.cpp filent.cpp filesys.cpp fileunix.cpp frames.cpp function.cpp glob.cpp hash.cpp hcache.cpp hdrmacro.cpp headers.cpp jam_strings.cpp jam.cpp jamgram.cpp lists.cpp make.cpp make1.cpp md5.cpp mem.cpp modules.cpp native.cpp option.cpp output.cpp parse.cpp pathnt.cpp pathsys.cpp pathunix.cpp regexp.cpp rules.cpp scan.cpp search.cpp startup.cpp tasks.cpp timestamp.cpp value.cpp variable.cpp w32_getreg.cpp mod_command_db.cpp mod_db.cpp mod_jam_builtin.cpp mod_jam_class.cpp mod_jam_errors.cpp mod_jam_modules.cpp mod_order.cpp mod_path.cpp mod_property_set.cpp mod_regex.cpp mod_sequence.cpp mod_set.cpp mod_string.cpp mod_summary.cpp mod_sysinfo.cpp mod_version.cpp -o b2

tools/build/src/engine/b2
Unicode/ICU support for Boost.Regex?... not found.
Generating B2 configuration in project-config.jam for gcc...

Bootstrapping is done. To build, run:

    ./b2
    
To generate header files, run:

    ./b2 headers

The configuration generated uses gcc to build by default. If that is
unintended either use the --with-toolset option or adjust configuration, by
editing 'project-config.jam'.

Further information:

   - Command line help:
     ./b2 --help
     
   - Getting started guide: 
     http://www.boost.org/more/getting_started/unix-variants.html
     
   - B2 documentation:
     http://www.boost.org/build/

          结束后当前目录下会多出一个”b2“文件

        4. 安装

        在当前目录下执行b2引擎进行进一步安装,安装iostream和random就足够了

[root@bogon boost]# ./b2 --with-iostreams --with-random install
…… ………
…… ………
common.copy /usr/local/include/boost/asio/detail/impl/buffer_sequence_adapter.ipp
common.copy /usr/local/include/boost/asio/detail/impl/descriptor_ops.ipp
gcc.compile.c++ bin.v2/libs/random/build/gcc-8/release/x86_64/link-static/threading-multi/visibility-hidden/random_device.o
gcc.compile.c++ bin.v2/libs/random/build/gcc-8/release/x86_64/threading-multi/visibility-hidden/random_device.o
gcc.archive bin.v2/libs/random/build/gcc-8/release/x86_64/link-static/threading-multi/visibility-hidden/libboost_random.a
common.copy /usr/local/lib/libboost_random.a
common.copy /usr/local/lib/cmake/boost_random-1.86.0/libboost_random-variant-static.cmake
gcc.link.dll bin.v2/libs/random/build/gcc-8/release/x86_64/threading-multi/visibility-hidden/libboost_random.so.1.86.0
common.copy /usr/local/lib/libboost_random.so.1.86.0
common.copy /usr/local/lib/cmake/boost_random-1.86.0/boost_random-config.cmake
common.copy /usr/local/lib/cmake/boost_random-1.86.0/boost_random-config-version.cmake
ln-UNIX /usr/local/lib/libboost_random.so
common.copy /usr/local/lib/cmake/boost_random-1.86.0/libboost_random-variant-shared.cmake
gcc.compile.c++ bin.v2/libs/iostreams/build/gcc-8/release/x86_64/threading-multi/visibility-hidden/zstd.o
gcc.link.dll bin.v2/libs/iostreams/build/gcc-8/release/x86_64/threading-multi/visibility-hidden/libboost_iostreams.so.1.86.0
common.copy /usr/local/lib/libboost_iostreams.so.1.86.0
ln-UNIX /usr/local/lib/libboost_iostreams.so
common.copy /usr/local/lib/cmake/boost_iostreams-1.86.0/boost_iostreams-config.cmake
common.copy /usr/local/lib/cmake/boost_iostreams-1.86.0/libboost_iostreams-variant-shared.cmake
common.copy /usr/local/lib/cmake/boost_iostreams-1.86.0/boost_iostreams-config-version.cmake

         5. 刷新动态库

        前提是/usr/local/lib64已经在/etc/ld.so.conf中了,这个刷新才能有效。

[root@bogon boost]# ldconfig

        参考:

        linux ldconfig命令,环境变量文件配置详解-CSDN博客

        6. 安装CMake

        既然上文已经提到boost要和cmake配合工作,当然hyperscan的编译是需要cmake的:

[root@bogon hyperscan]# yum install cmake -y
上次元数据过期检查:1 day, 18:47:07 前,执行于 2024年09月26日 星期四 09时33分49秒。
依赖关系解决。
===================================================================================================================================================================================================================
 软件包                                                 架构                                         版本                                                    仓库                                             大小
===================================================================================================================================================================================================================
安装:
 cmake                                                  x86_64                                       3.26.5-2.el8                                            appstream                                        14 M
安装依赖关系:
 cmake-data                                             noarch                                       3.26.5-2.el8                                            appstream                                       2.6 M
 cmake-filesystem                                       x86_64                                       3.26.5-2.el8                                            appstream                                        52 k
 cmake-rpm-macros                                       noarch                                       3.26.5-2.el8                                            appstream                                        44 k
 libuv                                                  x86_64                                       1:1.41.1-1.el8_4                                        appstream                                       156 k

事务概要
===================================================================================================================================================================================================================
安装  5 软件包

总下载:16 M
安装大小:45 M
下载软件包:
(1/5): cmake-filesystem-3.26.5-2.el8.x86_64.rpm                                                                                                                                    150 kB/s |  52 kB     00:00    
(2/5): cmake-rpm-macros-3.26.5-2.el8.noarch.rpm                                                                                                                                    210 kB/s |  44 kB     00:00    
(3/5): libuv-1.41.1-1.el8_4.x86_64.rpm                                                                                                                                             479 kB/s | 156 kB     00:00    
(4/5): cmake-data-3.26.5-2.el8.noarch.rpm                                                                                                                                          2.8 MB/s | 2.6 MB     00:00    
(5/5): cmake-3.26.5-2.el8.x86_64.rpm                                                                                                                                               7.3 MB/s |  14 MB     00:01    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
总计                                                                                                                                                                               8.8 MB/s |  16 MB     00:01     
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
  准备中  :                                                                                                                                                                                                    1/1 
  安装    : cmake-rpm-macros-3.26.5-2.el8.noarch                                                                                                                                                               1/5 
  安装    : cmake-filesystem-3.26.5-2.el8.x86_64                                                                                                                                                               2/5 
  安装    : libuv-1:1.41.1-1.el8_4.x86_64                                                                                                                                                                      3/5 
  安装    : cmake-data-3.26.5-2.el8.noarch                                                                                                                                                                     4/5 
  安装    : cmake-3.26.5-2.el8.x86_64                                                                                                                                                                          5/5 
  运行脚本: cmake-3.26.5-2.el8.x86_64                                                                                                                                                                          5/5 
  验证    : cmake-3.26.5-2.el8.x86_64                                                                                                                                                                          1/5 
  验证    : cmake-data-3.26.5-2.el8.noarch                                                                                                                                                                     2/5 
  验证    : cmake-filesystem-3.26.5-2.el8.x86_64                                                                                                                                                               3/5 
  验证    : cmake-rpm-macros-3.26.5-2.el8.noarch                                                                                                                                                               4/5 
  验证    : libuv-1:1.41.1-1.el8_4.x86_64                                                                                                                                                                      5/5 

已安装:
  cmake-3.26.5-2.el8.x86_64          cmake-data-3.26.5-2.el8.noarch          cmake-filesystem-3.26.5-2.el8.x86_64          cmake-rpm-macros-3.26.5-2.el8.noarch          libuv-1:1.41.1-1.el8_4.x86_64         

完毕!

(三)安装HyperScan

         1. 下载

        如上文所述,在github上下载,可能需要魔法 Releases · intel/hyperscan (github.com)

         2. 解压

[root@bogon opt]# tar zxvf hyperscan-5.4.2.tar.gz 
hyperscan-5.4.2/
hyperscan-5.4.2/.clang-format
hyperscan-5.4.2/.gitignore
hyperscan-5.4.2/CHANGELOG.md
hyperscan-5.4.2/CMakeLists.txt
hyperscan-5.4.2/COPYING
hyperscan-5.4.2/LICENSE
hyperscan-5.4.2/README.md
hyperscan-5.4.2/chimera/
hyperscan-5.4.2/chimera/CMakeLists.txt
hyperscan-5.4.2/chimera/ch.h
hyperscan-5.4.2/chimera/ch_alloc.c
hyperscan-5.4.2/chimera/ch_alloc.h
hyperscan-5.4.2/chimera/ch_common.h
hyperscan-5.4.2/chimera/ch_compile.cpp
hyperscan-5.4.2/chimera/ch_compile.h
hyperscan-5.4.2/chimera/ch_database.c
hyperscan-5.4.2/chimera/ch_database.h
hyperscan-5.4.2/chimera/ch_internal.h
hyperscan-5.4.2/chimera/ch_runtime.c
……
……

         3. 构建

        在hyperscan下建一个输出目录,名字任意。比如mybuild,不过这个名字取得比较随意,后面编程中屡屡要构建头文件、库文件连接的时候,屡屡被这个土气随意没有特点的名字弄得很尴尬,不如仍然叫hyperscan的好。

        进入mybuild执行cmake,注意cmake命令后面有两个“.”,代表cmake的起点在mybuild的上级目录的意思。

[root@bogon hyperscan]# mkdir mybuild
[root@bogon hyperscan]# ls
CHANGELOG.md  chimera  cmake  CMakeLists.txt  COPYING  doc  examples  hs.def  hs_runtime.def  include  libhs.pc.in  LICENSE  mybuild  README.md  src  tools  unit  util
[root@bogon hyperscan]# cd mybuild

[root@bogon mybuild]# cmake -DBUILD_SHARED_LIBS=on -DCMAKE_BUILD_TYPE=Release ..
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 2.8.12 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


-- The C compiler identification is GNU 8.5.0
-- The CXX compiler identification is GNU 8.5.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
……
……
-- Found Threads: TRUE  
-- Checking for module 'sqlite3'
--   Package 'sqlite3', required by 'virtual:world', not found
-- looking for sqlite3 in source tree
--   no sqlite3 in source tree
-- sqlite3 not found, not building hsbench
-- PCRE 8.41 not found, not building hscollider
-- Configuring done (4.2s)
-- Generating done (0.0s)
-- Build files have been written to: /opt/hyperscan/mybuild

         4. 编译

        cmake结束后,在mybuild目录下执行make进行编译,-j8代表启动8个任务进行并行编译

[root@bogon mybuild]# make -j8
……
……
[ 87%] Building CXX object CMakeFiles/hs_compile_shared.dir/src/util/report_manager.cpp.o
[ 87%] Building CXX object CMakeFiles/hs_compile_shared.dir/src/util/target_info.cpp.o
[ 88%] Building CXX object CMakeFiles/hs_compile_shared.dir/src/util/ue2string.cpp.o
[ 89%] Building C object CMakeFiles/hs_runtime_shared.dir/src/hs_version.c.o
[ 89%] Building C object CMakeFiles/hs_runtime_shared.dir/src/hs_valid_platform.c.o
[ 89%] Linking C shared library lib/libhs_runtime.so
[ 89%] Built target hs_runtime_shared
[ 89%] Built target hs_compile_shared
[ 90%] Building C object CMakeFiles/hs_shared.dir/src/hs_valid_platform.c.o
[ 90%] Building C object CMakeFiles/hs_shared.dir/src/hs_version.c.o
[ 90%] Linking CXX shared library lib/libhs.so
[ 90%] Built target hs_shared
[ 90%] Building C object examples/CMakeFiles/simplegrep.dir/simplegrep.c.o
[ 90%] Building CXX object tools/hscheck/CMakeFiles/hscheck.dir/main.cpp.o
[ 91%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/gtest/gtest-all.cc.o
[ 92%] Building CXX object examples/CMakeFiles/patbench.dir/patbench.cc.o
[ 92%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/allocators.cpp.o
[ 92%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/arg_checks.cpp.o
[ 92%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/bad_patterns.cpp.o
[ 92%] Building CXX object examples/CMakeFiles/pcapscan.dir/pcapscan.cc.o
[ 92%] Linking C executable ../bin/simplegrep
[ 92%] Built target simplegrep
[ 93%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/behaviour.cpp.o
[ 93%] Linking CXX executable ../bin/pcapscan
[ 93%] Built target pcapscan
[ 93%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/expr_info.cpp.o
[ 93%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/extparam.cpp.o
[ 93%] Linking CXX executable ../bin/patbench
[ 93%] Built target patbench
[ 94%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/identical.cpp.o
[ 95%] Linking CXX executable ../../bin/hscheck
[ 95%] Built target hscheck
[ 95%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/literals.cpp.o
[ 95%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/logical_combination.cpp.o
[ 96%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/main.cpp.o
[ 96%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/multi.cpp.o
[ 96%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/order.cpp.o
[ 96%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/scratch_op.cpp.o
[ 97%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/scratch_in_use.cpp.o
[ 97%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/serialize.cpp.o
[ 97%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/single.cpp.o
[ 98%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/som.cpp.o
[ 98%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/stream_op.cpp.o
[ 98%] Building CXX object unit/CMakeFiles/unit-hyperscan.dir/hyperscan/test_util.cpp.o
[100%] Linking CXX executable ../bin/unit-hyperscan
[100%] Built target unit-hyperscan

         5. 安装

[root@bogon mybuild]# make install
[ 48%] Built target hs_compile_shared
[ 59%] Built target hs_exec_shared_core2
[ 71%] Built target hs_exec_shared_corei7
[ 84%] Built target hs_exec_shared_avx2
[ 85%] Built target hs_exec_common_shared
[ 86%] Built target hs_runtime_shared
[ 86%] Built target ragel_Parser
[ 87%] Built target hs_shared
[ 87%] Built target ragel_ExpressionParser
[ 88%] Built target expressionutil
[ 89%] Built target corpusomatic
[ 89%] Built target databaseutil
[ 90%] Built target crosscompileutil
[ 97%] Built target unit-hyperscan
[ 98%] Built target hscheck
[ 98%] Built target simplegrep
[100%] Built target pcapscan
[100%] Built target patbench
Install the project...
-- Install configuration: "RELEASE"
-- Installing: /usr/local/lib64/pkgconfig/libhs.pc
-- Installing: /usr/local/include/hs/hs.h
-- Installing: /usr/local/include/hs/hs_common.h
-- Installing: /usr/local/include/hs/hs_compile.h
-- Installing: /usr/local/include/hs/hs_runtime.h
-- Installing: /usr/local/lib64/libhs_runtime.so.5.4.2
-- Installing: /usr/local/lib64/libhs_runtime.so.5
-- Installing: /usr/local/lib64/libhs_runtime.so
-- Installing: /usr/local/lib64/libhs.so.5.4.2
-- Installing: /usr/local/lib64/libhs.so.5
-- Installing: /usr/local/lib64/libhs.so
-- Installing: /usr/local/share/doc/hyperscan/examples/simplegrep.c
-- Installing: /usr/local/share/doc/hyperscan/examples/pcapscan.cc
-- Installing: /usr/local/share/doc/hyperscan/examples/patbench.cc
-- Installing: /usr/local/share/doc/hyperscan/examples/README.md
[root@bogon mybuild]# ls
bin  CMakeCache.txt  CMakeFiles  cmake_install.cmake  config.h  doc  examples  help-dummy.o  hs_version.h  install_manifest.txt  lib  libhs.pc  Makefile  src  tools  unit  util
[root@bogon mybuild]# ldconfig

        查看是否安装成功

        成功的情况下 mybuild下面会有lib,以及bin

        bin下有单元测试工具

[root@bogon bin]# pwd
/opt/hyperscan/mybuild/bin
[root@bogon bin]# ls
hscheck  patbench  pcapscan  simplegrep  unit-hyperscan

         6. 测试

        执行单元测试工具,如下这样基本就是安装成功了

[root@bogon mybuild]# bin/unit-hyperscan 
[==========] Running 4130 tests from 33 test cases.
[----------] Global test environment set-up.
[----------] 9 tests from CustomAllocator
[ RUN      ] CustomAllocator.DatabaseInfoBadAlloc
[       OK ] CustomAllocator.DatabaseInfoBadAlloc (3 ms)
[ RUN      ] CustomAllocator.TwoAlignedCompile
[       OK ] CustomAllocator.TwoAlignedCompile (0 ms)
[ RUN      ] CustomAllocator.TwoAlignedCompileError
[       OK ] CustomAllocator.TwoAlignedCompileError (1 ms)
[ RUN      ] CustomAllocator.TwoAlignedDatabaseInfo
[       OK ] CustomAllocator.TwoAlignedDatabaseInfo (0 ms)
[ RUN      ] CustomAllocator.TwoAlignedSerialize
[       OK ] CustomAllocator.TwoAlignedSerialize (0 ms)
[ RUN      ] CustomAllocator.TwoAlignedDeserialize
[       OK ] CustomAllocator.TwoAlignedDeserialize (0 ms)
[ RUN      ] CustomAllocator.TwoAlignedAllocScratch
[       OK ] CustomAllocator.TwoAlignedAllocScratch (0 ms)
[ RUN      ] CustomAllocator.NullMallocExpressionInfo
[       OK ] CustomAllocator.NullMallocExpressionInfo (1 ms)
[ RUN      ] CustomAllocator.TwoAlignedExpressionInfo
[       OK ] CustomAllocator.TwoAlignedExpressionInfo (0 ms)
[----------] 9 tests from CustomAllocator (5 ms total)

[----------] 141 tests from HyperscanArgChecks
[ RUN      ] HyperscanArgChecks.ValidPlatform
[       OK ] HyperscanArgChecks.ValidPlatform (0 ms)
[ RUN      ] HyperscanArgChecks.Version
[       OK ] HyperscanArgChecks.Version (0 ms)
[ RUN      ] HyperscanArgChecks.SingleCompileBlockNoPattern
[       OK ] HyperscanArgChecks.SingleCompileBlockNoPattern (0 ms)
[ RUN      ] HyperscanArgChecks.SingleCompileStreamingNoPattern
[       OK ] HyperscanArgChecks.SingleCompileStreamingNoPattern (0 ms)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值