DPDK實踐之(1)dpdk基礎使用
Author: Once Day Date: 2024年5月19日
一位熱衷于Linux學習和開發的菜鳥,試圖譜寫一場冒險之旅,也許終點只是一場白日夢…
漫漫長路,有人對你微笑過嘛…
全系列文檔可參考專欄:Linux基礎知識_Once-Day的博客-CSDN博客
參考文章:
- DPDK總結(網卡初始化)_rte_eth_dev_count_hz5034的博客-CSDN博客
- DPDK之網卡收包流程 (qq.com)
- Git DPDK資料下載
- DPDK download
- Getting Started Guide for Linux — documentation (dpdk.org)
- Sample Applications User Guides — documentation (dpdk.org)
文章目錄
- DPDK實踐之(1)dpdk基礎使用
- 1. 概述
- 1.1 linux編譯方式
- 1.2 運行系統要求
- 1.3 二級進程支持
- 2. 源碼編譯
- 2.1 編譯方式
- 2.2 編譯步驟
- 3. 運行dpdk基礎程序
- 3.1 Linux用戶空間驅動
- 3.2 dpdk程序運行標準步驟
- 3.3 運行helloworld程序
- 3.4 DPDK程序支持valgrind
- 4. DPDK EAL參數
- 4.1 CPU核心相關參數介紹
- 4.2 網卡相關參數
- 4.3 內存相關參數
- 4.4 debug相關參數
- 4.5 雜項選項
- 4.6 Linux相關參數
- 5. 附錄
- 5.1 DPDK運行CPU
- 5.2 DPDK運行內存
- 5.3 dpdk遙測功能
1. 概述
DPDK(Data Plane Development Kit)是一個由 Intel 開發并開源的軟件庫和驅動集合,用于在 commodity hardware(通用硬件)上進行快速封包處理。DPDK 可以極大提高數據包處理速度和吞吐量,使得網絡應用能夠高效地在 CPU 上運行,而無需依賴昂貴的專有硬件或專門的網絡處理設備。
DPDK 的主要組成部分包括:
-
Core Libraries:提供了一組用于處理數據包的核心庫,包括內存、隊列、緩沖池、端口和流量管理等功能。
-
Poll Mode Drivers (PMDs):這些是為各種網絡接口卡(NICs)編寫的輪詢模式驅動程序,可以繞過內核,直接在用戶空間處理數據包。
-
Additional Libraries:除了核心庫和 PMDs,DPDK 還提供了一些附加的庫,用于實現更高級的網絡功能,如安全、流量管理和服務質量(QoS)等。
DPDK 也支持各種類型的處理器和操作系統,包括 Linux、FreeBSD 和 Windows,可以在 x86、Power 和 ARM 等架構上運行。
DPDK 官網文檔地址是:http://www.dpdk.org/doc。您可以在此找到如何安裝和使用 DPDK 的詳細指南,以及各種參考文檔和教程。
1.1 linux編譯方式
對于大多數平臺,使用基本的DPDK功能不需要特殊的BIOS設置。在x86上可能有一些BIOS設置先決條件:
- HPET計時器:如果你的應用程序需要使用高精度事件計時器(HPET),可能需要在BIOS中啟用此功能。
- 功率管理:某些DPDK應用程序可能會受益于更精細的功率管理。例如,可以在BIOS中啟用或禁用CPU的某些節能特性。
- 小數據包性能:對于一些需要處理大量小數據包的應用,可能需要在BIOS中調整某些設置以優化性能。例如,可能需要禁用CPU的某些節能特性,或者更改內存訪問策略。
對于編譯期,需要支持C11標準(包含atomic實現),以及一些配套的編譯工具(gcc/clang/pkg-config/pkgconf等等)。以下是在不同的Linux發行版中安裝這些開發工具的命令:
對于RHEL/Fedora系統:
sudo dnf groupinstall "Development Tools"
對于Ubuntu/Debian系統:
sudo apt update
sudo apt install build-essential
對于Alpine Linux:
apk add alpine-sdk bsd-compat-headers
pkg-config
是一個腳本,它的主要目的是幫助你在編譯過程中添加正確的編譯器和鏈接器的選項。比如,如果你的程序需要一個庫,需要知道這個庫的頭文件和庫文件的位置。pkg-config
可以幫你查詢這些信息。
在Debian或Ubuntu等基于Debian的系統中,可以使用以下命令來安裝pkg-config
:
sudo apt update
sudo apt install pkg-config
然后,你可以使用pkg-config
命令來查詢庫的信息。例如,下面的命令可以查詢libpng
庫的版本信息:
pkg-config --modversion libpng
pkgconf
是pkg-config
的一個替代品,提供了相似的功能,并且有一些額外的改進。例如,pkgconf
可以更好地處理庫之間的依賴關系。
在Debian或Ubuntu等基于Debian的系統中,你可以使用以下命令來安裝pkgconf
:
sudo apt update
sudo apt install pkgconf
然后,你可以像使用pkg-config
一樣使用pkgconf
命令。
此外也需要以下依賴工具:
- Python 3.6 or later.
- Meson (version 0.53.2+) and ninja。
Meson
是一個開源的構建系統,旨在提供一個高效、用戶友好的構建環境。Ninja
是一個小型的構建系統,專注于速度,常常被用作Meson
和其他構建系統(如CMake
)的后端。 pyelftools
(version 0.22+)。pyelftools
是一個Python庫,用于解析和操作ELF (Executable and Linkable Format) 和 DWARF (Debug With Arbitrary Record Format) 格式的文件。這些格式被廣泛用于可執行文件、目標文件、共享庫和核心轉儲。- Library for handling NUMA (Non Uniform Memory Access).
libnuma-dev
in Debian/Ubuntu。
一些工具和庫是可選的,可以用來增強DPDK的功能或者使其能支持更多的硬件特性。
-
Intel? C++ Compiler (icc):這是Intel公司的C++編譯器,可以充分利用Intel處理器的特性來優化代碼。你可以在Intel官網找到安裝指南和文檔。在安裝icc時,可能需要安裝一些額外的庫。
-
IBM? Advance ToolChain for Powerlinux:這是一個開源的開發工具集和運行時庫,可以讓用戶在Linux系統上充分利用IBM最新的POWER硬件特性。你可以在IBM官網找到安裝文檔。
對于DPDK的一些組件,如庫和poll-mode drivers (PMDs),可能需要額外的依賴。在構建DPDK時,這些依賴會被自動檢測,并且會相應地啟用或禁用相關的組件。
在所有情況下,都需要相應的庫開發包(-devel或-dev)來構建DPDK組件。
例如,這些額外的依賴包括:
- libarchive:一些單元測試使用tar獲取它們的資源。
- libelf:編譯和使用bpf庫。
對于每個poll-mode driver,其額外的依賴可以在相關的DPDK指南文檔中找到:
- Network Interface Controller Drivers — documentation (dpdk.org)
1.2 運行系統要求
參考文檔:2. System Requirements — Data Plane Development Kit 24.03.0 documentation (dpdk.org)
下面是運行DPDK應用的基本系統需求:
- Kernel version >= 4.14,Linux系統內核版本至少4.14之上。
- glibc >= 2.7 (for features related to cpuset),Glibc版本至少在2.7之上。
- Linux內核配置要求HUGETLBFS、PROC_PAGE_MONITOR support。
- 如果HPET使能,則Linux內核配置要求HPET和HPET_MMAP也使能,可見High Precision Event Timer (HPET) Functionality。
Hugepages是一個在內核級別支持的特性,它可以使系統使用大于默認的4KB的內存頁大小。使用Hugepages可以增加性能,因為需要的頁數更少,因此需要更少的Translation Lookaside Buffers (TLBs)。TLB是一種高速的地址翻譯緩存,它能減少從虛擬頁地址到物理頁地址的轉換時間。如果不使用Hugepages,標準的4KB頁大小會導致高TLB miss比率,進而降低性能。
在Linux系統中,可以通過下面的步驟啟用和配置Hugepages:
-
檢查內核支持:首先,需要確認Linux內核支持Hugepages。可以通過查看
/proc/meminfo
文件來確認這一點。在這個文件中,如果看到像HugePages_Total
、HugePages_Free
和Hugepagesize
這樣的行,那么內核支持Hugepages。 -
配置Hugepages:可以通過
sysctl
命令或直接修改/etc/sysctl.conf
文件來配置Hugepages的數量。例如,如果想設置Hugepages的數量為1024,可以將下面的行添加到/etc/sysctl.conf
文件中:vm.nr_hugepages = 1024
然后,可以運行
sudo sysctl -p
命令來應用這個更改。 -
掛載Hugepages文件系統:最后需要掛載一個Hugepages文件系統。可以通過
mount
命令來完成這個操作,如下:sudo mkdir -p /mnt/huge sudo mount -t hugetlbfs nodev /mnt/huge
也可以將上面的行添加到
/etc/fstab
文件中,以便在系統啟動時自動掛載Hugepages文件系統。
在運行時以及啟動時預留hugepages。在許多情況下,預留hugepages可以優化高性能計算和網絡應用的性能。
-
在運行時預留hugepages:可以通過向
/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
文件寫入需要的hugepages數量來在系統運行時預留hugepages。這對于單節點系統來說很簡單。在多NUMA系統中,可以如下為NUMA節點單獨預留hugepages。# 預留1024 * 2 MB的大頁內存 echo 1024 > /sys/devices/system/node/nodeX/hugepages/hugepages-2048kB/nr_hugepages
-
在啟動時預留hugepages:在某些情況下,可能需要在系統啟動時預留hugepages。這可以通過在內核命令行中添加參數來完成。例如,對于2MB的頁,可以使用
hugepages=1024
參數。對于1GB的頁,需要顯式指定大小,如:default_hugepagesz=1G hugepagesz=1G hugepages=4
一些內核不允許在運行時收集1G大小的內存頁,因此需要在啟動時預留,此時另外一個好處是內存物理地址也能更加連續。
-
DPDK Hugepages管理工具:DPDK提供了一個工具
dpdk-hugepages.py
,可以用來管理hugepages。 -
Hugepages大小的支持:支持的hugepages大小取決于CPU的架構。在Intel架構中,如果CPU標志中存在
pse
,則支持2MB的hugepages;如果存在pdpe1gb
,則支持1GB的hugepages。在IBM Power架構中,支持的hugepages大小為16MB和16GB。 -
64位應用的建議:對于64位應用,如果平臺支持,推薦使用1GB的hugepages。
-
NUMA系統中的分配:在雙插槽NUMA系統中,啟動時預留的hugepages數量通常在兩個插槽之間平均分配(假設兩個插槽上都有足夠的內存)。
對于更多的內核選項,可以在Linux源代碼樹的 Documentation/admin-guide/kernel-parameters.txt
文件中查看。
1.3 二級進程支持
參考文檔:2. System Requirements — Data Plane Development Kit 24.03.0 documentation (dpdk.org)
在DPDK(數據平面開發套件)中,二級進程(Secondary Process)是一種特殊的進程,它可以與主進程(Primary Process)共享某些資源,例如內存,隊列等。在很多場景下,主進程負責初始化和啟動一些服務,例如內存管理,設備管理等。然后,二級進程可以利用這些已經初始化的資源進行一些特定的任務。
這種設計使得在一個系統中可以運行多個進程,而不需要每個進程都進行一次完整的初始化。這樣就可以降低啟動時間,減少資源使用,提高整體性能。
在DPDK中,二級進程通常通過使用rte_eal_init
函數的特定參數啟動,這些參數使得二級進程能夠找到并連接到主進程已經初始化的資源。
如果不需要二級進程支持,DPDK可以通過使用“in-memory”模式無需任何配置即可使用大頁。如果需要二級進程支持,需要創建大頁的掛載點。在現代Linux發行版上,系統提供了一個默認的大頁掛載點,位于/dev/hugepages。這個掛載點將使用上述內核參數設置的默認大頁大小。
然而,為了使用默認以外的大頁大小,需要手動為這些大頁大小(例如1GB頁)創建掛載點。為了使大小為1GB的大頁可供DPDK使用,必須執行以下步驟:
mkdir /mnt/huge
mount -t hugetlbfs pagesize=1GB /mnt/huge
可以通過在/etc/fstab文件中添加以下行,使掛載點在重啟后保持永久:
nodev /mnt/huge hugetlbfs pagesize=1GB 0 0
2. 源碼編譯
2.1 編譯方式
有多種編譯方式,這里以Linux平臺X86本地編譯為主,各種編譯方式鏈接如下:
- 3. Compiling the DPDK Target from Source — Data Plane Development Kit 23.11.0-rc1 documentation
- 4. Cross compiling DPDK for aarch64 and aarch32 — Data Plane Development Kit 23.11.0-rc1 documentation
- 5. Cross compiling DPDK for LoongArch — Data Plane Development Kit 23.11.0-rc1 documentation
- 6. Cross compiling DPDK for RISC-V — Data Plane Development Kit 23.11.0-rc1 documentation
2.2 編譯步驟
首先在官網下載源碼文件(也可以在gitlab下載),選擇合適的版本,然后進行解壓:
onceday@book2023:~/dpdk-compile$ tar -xJf dpdk-22.11.3.tar.xz
onceday@book2023:~/dpdk-compile$ cd dpdk-stable-22.11.3/
以下是這些目錄更詳細的解釋:
doc
目錄,包含了DPDK的官方文檔,如安裝指南、程序員手冊、API文檔等。license
目錄,包含了DPDK的許可證信息。lib
目錄,包含了構成DPDK的各種庫的源代碼,例如內存管理、緩沖池、隊列、以太網控制器等。drivers
目錄,包含了DPDK的輪詢模式驅動程序的源代碼,這些驅動程序主要用于網絡接口卡。app
目錄,包含了DPDK的應用程序的源代碼,這些應用程序主要用于自動測試DPDK的各種功能。examples
目錄,包含了一些DPDK應用程序的示例源代碼,這些示例可以幫助開發者更好地理解和使用DPDK。config
和buildtools
目錄,包含了一些與構建和配置DPDK相關的腳本和配置文件。usertools
目錄,包含了一些腳本,這些腳本可以幫助DPDK的最終用戶更方便地使用DPDK應用程序。devtools
目錄,包含了一些腳本,這些腳本主要供DPDK的開發者在開發過程中使用。kernel
目錄,包含了一些內核模塊的源代碼,這些內核模塊在某些操作系統上是必需的,例如Linux。
要配置DPDK構建,可以使用:
meson setup <options> build
其中,“build”是所需的輸出構建目錄,<options>
可以為空或是meson或DPDK特定的構建選項之一。
配置好之后,要構建并安裝DPDK到系統中,使用:
cd build
ninja
sudo meson install
sudo ldconfig
上面的最后兩個命令通常需要以root身份運行,meson install
步驟將構建的對象復制到最終的系統范圍的位置,最后一步使動態加載器ld.so
更新其緩存以考慮新的對象。
在某些Linux發行版中,例如Fedora或Redhat,/usr/local
中的路徑不在加載器的默認路徑中。因此,在這些發行版上,應在運行ldconfig
之前將/usr/local/lib
和/usr/local/lib64
添加到/etc/ld.so.conf.d/
目錄中的一個文件里。你可以通過以下命令來做到這一點:
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/local_lib.conf
echo "/usr/local/lib64" | sudo tee -a /etc/ld.so.conf.d/local_lib.conf
sudo ldconfig
這樣,加載器就可以在/usr/local/lib
和/usr/local/lib64
中查找動態鏈接庫了。
3. 運行dpdk基礎程序
3.1 Linux用戶空間驅動
參考文檔:7. Linux Drivers — Data Plane Development Kit 24.03.0 documentation (dpdk.org)
DPDK 支持多種網卡驅動,包括 Intel IXGBE、Intel I40E、Intel ICE、Mellanox MLX4、Mellanox MLX5、Cisco enic 等。DPDK使用 UIO (Userspace I/O) 驅動或 VFIO (Virtual Function I/O) 驅動來實現用戶態驅動,繞過內核網絡協議棧,以提高數據包處理性能。
- UIO 驅動包括
igb_uio
和uio_pci_generic
,它們是內核模塊,用于將網卡設備綁定到用戶空間。 - VFIO 驅動是一種更新的用戶態驅動方式,它利用 IOMMU 實現設備直接分配給用戶空間,提供更好的性能和安全性。
使用 dpdk-devbind.py
構建 DPDK 驅動環境:
dpdk-devbind.py
是 DPDK 提供的一個工具,用于管理網卡設備的驅動綁定。- 首先,確保已經加載了所需的內核模塊,如
uio
、igb_uio
或vfio-pci
。 - 使用
dpdk-devbind.py --status
命令查看當前系統中的網卡設備及其綁定的驅動。 - 使用
dpdk-devbind.py --bind
命令將網卡設備綁定到 DPDK 支持的用戶態驅動,例如:
這將把sudo dpdk-devbind.py --bind=uio_pci_generic eth0
eth0
網卡綁定到uio_pci_generic
驅動。 - 如果需要將網卡設備恢復到內核驅動,可以使用
--unbind
選項:sudo dpdk-devbind.py --unbind eth0
- 綁定完成后,可以再次使用
dpdk-devbind.py --status
命令確認網卡設備已經成功綁定到了目標驅動。
在使用 DPDK 時,綁定的網卡設備將無法被內核網絡協議棧使用,因此在配置時需要謹慎選擇要綁定的網卡設備,以免影響系統的正常網絡功能。
下面是一臺虛擬云服務器的驅動查詢情況:
ubuntu->build:$ sudo dpdk-devbind.py --statusNetwork devices using kernel driver
===================================
0000:00:05.0 'Virtio network device 1000' if=eth0 drv=virtio-pci unused=vfio-pci *Active*
eth0是一個虛擬接口,驅動是virtio-pci,可以更換為(危險操作)vfio-pci,然后dpdk就可以使用該接口。但是這里不建議這么操作,除非在本地設備上創建虛擬機完成這個操作。
一般的家用PC網卡驅動,dpdk都不支持用戶空間驅動,因此后面用dpdk自行創建的虛擬接口完成測試,如下:
(1) TAP (Terminal Access Point) 設備是一種虛擬網絡接口,模擬了一個以太網設備,可以與物理網絡設備類似地進行數據包的發送和接收。
-
在 DPDK 中,可以使用
net_tap
PMD (Poll Mode Driver) 創建 TAP 設備。通過--vdev
參數指定net_tap
設備,并提供相關的配置選項,例如:sudo testpmd -c 0x3 -n 4 --vdev 'net_tap0,iface=tap0' -- -i
這個命令會創建一個名為
net_tap0
的虛擬接口,并將其與tap0
這個 TAP 設備關聯。 -
TAP 設備創建后,可以像物理網卡一樣進行配置,例如設置 IP 地址、子網掩碼等。
-
DPDK 應用程序可以通過
rte_eth_rx_burst
和rte_eth_tx_burst
等 API 函數來接收和發送 TAP 設備上的數據包。 -
TAP 設備的優點是配置簡單,容易與現有的網絡環境集成。它可以與其他虛擬化技術(如 QEMU、VirtualBox 等)配合使用,實現虛擬機與 DPDK 應用程序之間的網絡通信。
(2) virtio 接口,virtio 是一種用于虛擬化環境的通用 I/O 框架,它提供了一組標準的接口和協議,用于在虛擬機和宿主機之間高效地傳輸數據。
- DPDK 支持使用
virtio-user
后端創建基于 virtio 的虛擬接口。通過--vdev
參數指定net_virtio_user
設備,并提供相關的配置選項,例如:
這個命令會創建一個名為sudo testpmd -c 0x3 -n 4 --vdev 'net_virtio_user0,path=/tmp/vhost-sock0' -- -i
net_virtio_user0
的虛擬接口,并將其與/tmp/vhost-sock0
這個 Unix 域套接字關聯。 - virtio 接口的數據傳輸是基于共享內存的,通過在虛擬機和宿主機之間共享內存區域來實現高效的數據傳輸。
- DPDK 應用程序可以通過
rte_eth_rx_burst
和rte_eth_tx_burst
等 API 函數來接收和發送 virtio 接口上的數據包。 - virtio 接口的優點是性能高,可以利用虛擬化技術提供的硬件加速功能,如 SR-IOV、IOMMU 等,實現近乎裸金屬的性能。
- virtio 接口常用于 DPDK 應用程序與虛擬機的高性能網絡通信場景,例如在 NFV (網絡功能虛擬化)中,通過 virtio 接口將虛擬機中的網絡流量高效地轉發給 DPDK 應用程序進行處理。
TAP 設備配置簡單,易于與現有網絡環境集成,而 virtio 接口則提供了更高的性能,適用于對網絡性能要求較高的場景。
3.2 dpdk程序運行標準步驟
DPDK應用的啟動通常涉及以下幾個步驟:
-
環境設置:在運行DPDK應用之前,需要設置一些環境變量,例如,對Hugepages的配置。這可以通過在啟動腳本中設置
nr_hugepages
參數來完成。echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
-
綁定驅動:DPDK應用需要直接訪問網絡設備。這需要將網絡設備的驅動從常規的Linux網絡驅動(例如
e1000
)更改為DPDK支持的用戶空間驅動,如uio_pci_generic
、igb_uio
或vfio
。首先,加載適當的UIO模塊,例如
igb_uio
:sudo modprobe uio sudo insmod kmod/igb_uio.ko
然后,找出要綁定到DPDK的網絡設備的PCI地址。你可以使用
lspci
命令來查找,例如:lspci | grep Ethernet
設備的PCI地址應該類似于
01:00.0
或02:00.0
。最后,使用
dpdk-devbind.py
工具將設備綁定到igb_uio
驅動:sudo python3 usertools/dpdk-devbind.py --bind=igb_uio 01:00.0
-
啟動應用:使用
sudo
啟動應用時,將PCI設備的地址作為參數傳遞給應用。sudo ./build/l2fwd -l 0-3 -n 4 -- -q 8 -p 0x1
3.3 運行helloworld程序
默認的dpdk編譯里沒有包含helloworld這個示例程序,因此這里先添加helloworld編譯選項,如下:
在dpdk源碼界面輸入以下命令:
sudo meson configure build -Dexamples=helloworld
cd build
sudo ninja
然后就可以在examples目錄下面看到編譯出來的可執行程序:
ubuntu->build:$ ll examples/
-rwxr-xr-x 1 root root 32900088 May 19 17:12 dpdk-helloworld*
先嘗試直接運行,會出現錯誤:
ubuntu->build:$ ./examples/dpdk-helloworld
EAL: Detected CPU lcores: 4
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /run/user/1000/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'VA'
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
6: [./examples/dpdk-helloworld(_start+0x25) [0x55b1030fcda5]]
5: [/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f818377ae40]]
4: [/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f818377ad90]]
3: [./examples/dpdk-helloworld(+0x1f130c) [0x55b1023e630c]]
2: [./examples/dpdk-helloworld(__rte_panic+0xcd) [0x55b102402293]]
1: [./examples/dpdk-helloworld(rte_dump_stack+0x32) [0x55b10329cab2]]
Aborted
這里提示沒有找到可用的大頁內存,所以使用3.2節里的方法來保留大頁內存,如下(需要root權限):
onceday->~:# echo 128 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
然后使用管理員權限運行,如下:
ubuntu->build:$ sudo ./examples/dpdk-helloworld
EAL: Detected CPU lcores: 4
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: Probe PCI driver: net_virtio (1af4:1000) device: 0000:00:05.0 (socket 0)
eth_virtio_pci_init(): Failed to init PCI device
EAL: Requested device 0000:00:05.0 cannot be used
TELEMETRY: No legacy callbacks, legacy socket not created
hello from core 1
hello from core 2
hello from core 3
hello from core 0
可以看到,默認在所有核上運行一個線程,每個線程都輸出一個hello,這就是dpdk的per-core處理,能夠在每個核上綁定一個線程。此外,這里提示PCI驅動綁定失敗,這是因為運行之前沒有替換網卡驅動為DPDK支持的用戶空間驅動。
3.4 DPDK程序支持valgrind
在X86上,會因為AVX512指令集導致valgrind支持異常,根據stack-overflow回答,需要關閉dpdk編譯對avx512指令集的支持情況,可參考文檔:gcc - Disabling all AVX512 extensions - Stack Overflow。
這個avx512指令集通過一個python腳本檢測,直接修改代碼即可(直接返回錯誤):
#buildtools/binutils-avx512-check.py
import subprocess
import sys
import tempfileobjdump, *cc = sys.argv[1:]
with tempfile.NamedTemporaryFile() as obj:# On Windows, the file is opened exclusively and is not writable.sys.exit(1) # 這里直接返回錯誤obj.close()# from https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90028gather_params = '0x8(,%ymm1,1),%ymm0{%k2}'src = '__asm__("vpgatherqq {}");'.format(gather_params).encode('utf-8')subprocess.run(cc + ['-c', '-xc', '-o', obj.name, '-'], input=src, check=True)asm = subprocess.run([objdump, '-d', '--no-show-raw-insn', obj.name],stdout=subprocess.PIPE, check=True).stdout.decode('utf-8')if gather_params not in asm:print('vpgatherqq displacement error with as')sys.exit(1)
然后使用meson setup
后,生成的rte_build_config.h文件里就不會包含相關的指令集,即:
#define RTE_COMPILE_TIME_CPUFLAGS RTE_CPUFLAG_SSE,RTE_CPUFLAG_SSE2,RTE_CPUFLAG_SSE3,RTE_CPUFLAG_SSSE3,RTE_CPUFLAG_SSE4_1,RTE_CPUFLAG_SSE4_2,RTE_CPUFLAG_AES,RTE_CPUFLAG_AVX,RTE_CPUFLAG_AVX2,RTE_CPUFLAG_PCLMULQDQ,RTE_CPUFLAG_RDRAND,RTE_CPUFLAG_RDSEED
使用valgrind來檢測dpdk-helloworld的內存使用情況,如下:
ubuntu->build:$ sudo valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes ./examples/dpdk-helloworld
==2444979== Memcheck, a memory error detector
==2444979== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==2444979== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==2444979== Command: ./examples/dpdk-helloworld
==2444979==
EAL: Detected CPU lcores: 4
EAL: Detected NUMA nodes: 1
EAL: Detected static linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
......(省略大量內容)......
==2444979== LEAK SUMMARY:
==2444979== definitely lost: 0 bytes in 0 blocks
==2444979== indirectly lost: 0 bytes in 0 blocks
==2444979== possibly lost: 1,600 bytes in 5 blocks
==2444979== still reachable: 43,254 bytes in 445 blocks
==2444979== suppressed: 0 bytes in 0 blocks
==2444979==
==2444979== For lists of detected and suppressed errors, rerun with: -s
==2444979== ERROR SUMMARY: 19 errors from 14 contexts (suppressed: 0 from 0)
從運行結果來看,有很多泄露地方,這是因為dpdk庫并未考慮對一些持續存在的內存進行釋放,而且由于申請了太多的虛擬地址,導致valgrind的執行結果較久,效率較低。
如果想將valgrind完全用于dpdk,還需要進一步優化dpdk相關的函數,來減少這類誤報和執行效率問題。
4. DPDK EAL參數
參考文檔:9. EAL parameters — Data Plane Development Kit 24.03.0 documentation (dpdk.org):
4.1 CPU核心相關參數介紹
-c COREMASK/-l CORELIST
,用于指定應用程序使用的邏輯核。COREMASK是一個十六進制位掩碼,CORELIST是一個逗號分隔的邏輯核列表。例如,--lcores 0-3
表示將邏輯核心0-3映射到物理核心0-3。
--lcores <core map>
,該參數用于將邏輯核心集合映射到物理CPU核心集合。參數格式為:
<lcores[@cpus]>[<,lcores[@cpus]>...]
其中,lcores表示邏輯核心列表,cpus表示物理CPU核心列表。多個映射關系可以用逗號",“分隔。在每個映射關系中,可以用括號”()“將lcores和cpus括起來表示一個組。連字符”-“用于指定連續的核心編號范圍,逗號”,“用于分隔單個核心編號。如果一個組只有一個元素,可以省略括號。如果lcores和cpus的值相同,可以省略”@"符號。
例如,--lcores 0-3@(0-3),4@(4)
表示將邏輯核心0-3映射到物理核心0-3,將邏輯核心4映射到物理核心4。
--main-lcore <core ID>
,該參數用于指定主核心的ID。在DPDK應用程序中,通常會有一個主線程(main thread)用于執行初始化、配置和控制任務,而其他線程則用于處理數據包。–main-lcore參數就是用來指定運行主線程的核心ID。
-s <service core mask>
,該參數用于設置服務核心的十六進制掩碼。服務核心是DPDK應用程序中專門用于處理服務任務的核心,如定時器、警報等。這些任務通常與數據包處理無關,但是對于應用程序的正常運行非常重要。
4.2 網卡相關參數
-b, --block <[domain:]bus:devid.func
>,該參數用于阻止EAL(Environment Abstraction Layer)使用指定的PCI設備。通過指定設備的域(domain)、總線(bus)、設備ID(devid)和功能(func),可以跳過對該設備的探測,從而防止EAL使用它。可以使用多個-b
選項來阻止多個設備。
例如,"-b 0000:01:00.0"表示阻止EAL使用域0、總線1、設備ID為0、功能為0的PCI設備。
-a, --allow <[domain:]bus:devid.func
>,該參數用于將指定的PCI設備添加到EAL的探測列表中。與-b
選項類似,需要指定設備的域、總線、設備ID和功能。
例如,"-a 0000:02:00.0"表示將域0、總線2、設備ID為0、功能為0的PCI設備添加到EAL的探測列表中。
需要注意,-a選項不能與-b(阻止列表)選項同時使用。
--vdev <device arguments>
,該參數用于添加虛擬設備。虛擬設備是指不是物理存在的設備,而是由DPDK模擬的設備。–vdev參數的格式為<driver><id>[,key=val, ...]
,其中,driver是虛擬設備的驅動名稱,id是設備的唯一標識符,key=val是可選的參數。
例如,--vdev 'net_pcap0,rx_pcap=input.pcap,tx_pcap=output.pcap
表示添加一個名為net_pcap0的虛擬設備,該設備使用PCAP驅動,并指定了rx_pcap和tx_pcap兩個參數,分別表示接收和發送數據包的PCAP文件。
-d <path to shared object or directory>
,該參數用于加載外部驅動程序。可以指定單個共享對象文件的路徑,也可以指定包含多個驅動程序共享對象的目錄路徑。可以使用多個-d選項來加載多個驅動程序。
例如,-d /usr/local/lib/dpdk/drivers
表示加載/usr/local/lib/dpdk/drivers
目錄下的所有驅動程序。
--no-pci
,該參數用于禁用PCI總線。使用該參數后,EAL將不會探測和使用任何PCI設備。
4.3 內存相關參數
-n <number of channels>
,該參數用于設置內存通道的數量。在多通道內存系統中,可以通過增加通道數來提高內存帶寬和性能。EAL將根據指定的通道數對內存進行初始化和分配。
例如,-n 4
表示使用4個內存通道。
-r <number of ranks>
,該參數用于設置內存條(rank)的數量。內存條是指物理內存模塊,一個內存通道可以有多個內存條。EAL默認會自動檢測內存條的數量,但也可以通過-r參數手動指定。
例如,-r 2
表示每個內存通道有2個內存條。
-m <megabytes>
,該參數用于指定在啟動時預先分配的內存大小,單位為兆字節(MB)。EAL將在應用程序啟動時一次性分配指定大小的內存,以避免運行時頻繁的內存分配和釋放操作,提高性能。
例如,-m 1024
表示預先分配1024MB(即1GB)的內存。
--in-memory
,該參數用于指定EAL完全在內存中運行,不創建任何共享數據結構。這意味著EAL不會使用共享內存、共享文件等跨進程通信的機制,而是將所有數據都保存在進程的內存空間中。使用該參數可以提高性能,但也會增加內存消耗。
需要注意的是,--in-memory
參數隱含了--no-shconf
(不創建共享配置)和--huge-unlink
(如果適用,不創建大頁面文件)參數的效果。
--iova-mode <pa|va>
,該參數用于強制指定IOVA(I/O Virtual Address)模式。IOVA是用于進行DMA(直接內存訪問)操作的虛擬地址空間。EAL支持兩種IOVA模式:物理地址(pa)和虛擬地址(va)。
例如,--iova-mode pa
表示強制使用物理地址作為IOVA。
--huge-worker-stack[=size]
,該參數用于從大頁面內存中分配工作線程的棧空間。默認情況下,EAL使用系統的pthread棧大小作為工作線程的棧大小。使用--huge-worker-stack
參數可以將棧分配在大頁面內存中,以提高性能。可以通過可選的size參數指定棧的大小,單位為KB。
例如,--huge-worker-stack
表示使用大頁面內存分配工作線程棧,棧大小為默認的pthread棧大小。而"–huge-worker-stack=4096"表示使用大頁面內存分配工作線程棧,棧大小為4MB。
4.4 debug相關參數
--no-shconf
,該參數用于指定EAL不創建任何共享文件。這意味著EAL將不支持輔助進程(secondary process),因為輔助進程依賴于與主進程共享的配置文件和內存。使用該參數可以簡化EAL的配置和管理,但也會限制應用程序的功能。
--no-huge
,該參數用于指定EAL使用匿名內存而不是大頁面(hugepages)。與--no-shconf
參數類似,使用--no-huge
參數也會導致EAL不支持輔助進程,因為輔助進程通常與主進程共享大頁面內存。使用匿名內存可以簡化內存管理,但也可能影響性能。
--log-level <type:val>
,該參數用于為特定組件指定日志級別。可以多次使用該參數來設置不同組件的日志級別。參數格式為:--log-level <組件>:<級別>
,其中,<組件>
是EAL中的組件名稱,如lib.eal表示EAL庫;<級別>
是日志級別,如debug表示調試級別。例如,--log-level lib.eal:debug
表示將EAL庫的日志級別設置為調試級別。
--trace=<regex-match>
,該參數用于根據正則表達式匹配的跟蹤名稱啟用跟蹤功能。默認情況下,跟蹤功能是禁用的,需要通過--trace
參數顯式啟用。可以多次使用該參數,最多可以指定32個跟蹤配置。
例如,--trace=eal
表示啟用EAL組件的全局跟蹤配置,--trace=.*
表示啟用所有組件的全局跟蹤配置。
--trace-dir=<directory path>
,該參數用于指定跟蹤輸出的目錄路徑。默認情況下,跟蹤輸出文件會創建在用戶的主目錄中。可以通過--trace-dir
參數指定其他目錄。該參數只能指定一次。
例如,--trace-dir=/tmp
表示將/tmp目錄配置為跟蹤輸出目錄。
--trace-bufsz=<val>
,該參數用于指定每個線程的跟蹤輸出文件的最大大小。可以使用B、K、M等單位分別表示字節、千字節、兆字節。默認情況下,跟蹤輸出文件的大小為1MB。該參數只能指定一次。
例如,--trace-bufsz=2M
表示將跟蹤輸出文件的最大大小配置為2MB。
--trace-mode=<o[verwrite] | d[iscard]>
,該參數用于指定跟蹤輸出文件的更新模式。當文件大小達到最大限制時,可以選擇覆蓋(overwrite)或丟棄(discard)新的跟蹤更新。默認模式為覆蓋。該參數只能指定一次。
例如,“–trace-mode=d"或”–trace-mode=discard"表示當跟蹤輸出文件達到最大大小時,丟棄新的跟蹤更新。
4.5 雜項選項
-h
, --help
,顯示幫助信息,列出所有可用的 EAL (Environment Abstraction Layer) 參數。
-v
,在應用程序啟動時顯示版本信息。這對于確認您正在運行的 DPDK 版本很有幫助。
--mbuf-pool-ops-name
,指定 mbuf (內存緩沖區) 使用的池操作名稱。DPDK 使用 mbuf 來管理數據包緩沖區,不同的池操作可能針對不同的使用場景進行了優化。
--telemetry
,啟用遙測功能(默認啟用)。DPDK 的遙測功能可以收集運行時的性能指標和事件,幫助進行性能分析和優化。
--no-telemetry
,禁用遙測功能。
--force-max-simd-bitwidth=<val>
,指定處理器支持的最大 SIMD (Single Instruction, Multiple Data) 位寬。這將限制程序使用的向量指令集。例如:
--force-max-simd-bitwidth=512
: 允許使用最大 512 位的 SIMD 指令,即 AVX-512。--force-max-simd-bitwidth=64
: 將 SIMD 位寬限制為 64 位,實際上禁用了所有向量化代碼。--force-max-simd-bitwidth=0
: 禁用 SIMD 位寬限制。
4.6 Linux相關參數
--create-uio-dev
,為綁定到 igb_uio 內核驅動程序的設備創建 /dev/uioX
文件。通常由 igb_uio 驅動程序自行完成。例如,如果有兩個網卡綁定到了 igb_uio 驅動,那么會自動創建 /dev/uio0
和 /dev/uio1
文件。
--vmware-tsc-map
,使用 VMware TSC 映射而不是原生 RDTSC。在 VMware 虛擬機中運行 DPDK 應用時,使用該選項可以提高時間戳計數器的精確度。
--no-hpet
,不使用高精度事件計時器(HPET)。在某些系統上,禁用 HPET 可能有助于提高性能。
--vfio-intr <legacy|msi|msix>
,為綁定到 VFIO 內核驅動程序的設備使用指定的中斷模式。例如,--vfio-intr msix
表示使用 MSI-X 中斷模式,相比于舊的中斷模式,它可以支持更多的中斷向量。
--vfio-vf-token <uuid>
, 為綁定到 VFIO 內核驅動程序的設備使用指定的 VF 令牌。這在使用 SR-IOV 虛擬功能時很有用,可以確保不同的 DPDK 進程使用不同的虛擬功能。
--file-prefix <prefix name>
,為 DPDK 進程使用不同的共享數據文件前綴。這允許在不同前綴下運行多個獨立的 DPDK 主/從進程。例如,--file-prefix foo
將創建以 foo
開頭的共享數據文件,而不是默認的 rte
前綴。
--legacy-mem
,使用傳統的 DPDK 內存分配模式。在較新版本的 DPDK 中,默認使用更高效的內存分配方式。
--socket-mem <amounts of memory per socket>
,預分配每個 socket 指定數量的內存。例如,--socket-mem 1024,2048
將在 socket 0 上分配 1GB 內存,在 socket 1 上分配 2GB 內存。
--socket-limit <amounts of memory per socket>
,對每個 socket 的內存使用設置上限(僅限非傳統內存模式)。例如,--socket-limit 2048,4096
將 socket 0 的內存使用限制為 2GB,socket 1 限制為 4GB。值為 0 表示對特定 socket 禁用限制。
--single-file-segments
,在 hugetlbfs 中創建更少的文件(僅限非傳統模式)。這有助于減少 hugetlbfs 中的文件數量,并提高性能。
--huge-dir <path to hugetlbfs directory>
,使用指定的 hugetlbfs 目錄,而不是自動檢測的目錄。例如,--huge-dir /mnt/huge2M
表示使用 /mnt/huge2M
作為 hugetlbfs 目錄。
--huge-unlink[=existing|always|never]
,控制如何刪除 hugepage 文件。默認行為是刪除并重新創建現有的 hugepage 文件。--huge-unlink=always
表示總是刪除 hugepage 文件。--huge-unlink=never
表示從不刪除,而是重新映射,允許重用 hugepage。
--match-allocations
,按照原始分配的方式將 hugepage 釋放回系統。這有助于避免內存碎片問題。
--proc-type <primary|secondary|auto>
,設置當前進程的類型。primary 表示主進程,secondary 表示從進程,auto 表示自動檢測。
--base-virtaddr <address>
,嘗試為主 DPDK 進程的所有內存映射使用不同的起始地址。當從進程由于地址映射沖突而無法啟動時,這個選項很有幫助。例如,--base-virtaddr 0x7f000000000
將起始虛擬地址設置為 0x7f000000000。
5. 附錄
5.1 DPDK運行CPU
DPDK應用程序通過EAL(Environment Abstraction Layer)管理和配置邏輯核心。coremask (-c 0x0f)
或corelist (-l 0-3)
參數用于指定應用程序使用的邏輯核,每個參數位對應一個邏輯核號。在應用程序初始化時,EAL會顯示使用的邏輯核及其所在的物理CPU socket。了解邏輯核與物理CPU之間的映射關系,有助于優化NUMA架構下的性能。
DPDK應用程序采用輪詢而非中斷模式處理數據包,因此需要專門的worker邏輯核。除worker核外,EAL還使用一個master邏輯核做管理任務,master核通常與worker核分開,以免影響性能。
在高性能場景下,如OVS-DPDK,通常會將整個邏輯核專門綁定給DPDK使用,而不與其他程序共享,以最小化上下文切換開銷,實現最優數據面性能。這種使用獨占邏輯核的方式在生產中非常普遍。
5.2 DPDK運行內存
DPDK應用程序在啟動時可以通過-m
或--socket-mem
參數來指定使用的內存大小。如果沒有顯式傳遞這些參數,DPDK會自動使用與hugepage分配的內存大小相同的內存。
如果顯式傳遞了-m
或--socket-mem
參數,但請求的內存大小超過了預留的hugepage內存,應用程序會啟動失敗。另一方面,如果請求的內存小于預留的hugepage內存,尤其是使用-m
選項時,應用程序也可能會失敗。假設系統在socket 0
和socket 1
上分別預留了1024個2MB的hugepage。如果用戶請求128MB內存,這64個頁面可能無法滿足以下約束:
-
內核可能只在socket 1上給應用程序分配hugepage內存。在這種情況下,如果應用程序嘗試在socket 0上創建對象(如ring或memory pool),就會失敗。
-
這些頁面可以位于物理內存中的任何位置,盡管DPDK EAL會嘗試以連續塊的方式分配內存,但這些頁面可能不是連續的。在這種情況下,應用程序無法分配大的內存池。
為了避免上述問題,建議使用--socket-mem
選項而不是-m
選項。–socket-mem選項可用于為特定的socket請求指定數量的內存。
- 例如,
--socket-mem=0,512
表示僅為socket 1保留512MB內存。 - 在四個socket的系統上,要在socket 0和socket 2上各分配1GB內存,可以使用參數
--socket-mem=1024,0,1024
。未顯式引用的CPU socket(如本例中的socket 3)不會預留任何內存。
如果DPDK無法在每個socket上分配足夠的內存,EAL初始化將失敗。
5.3 dpdk遙測功能
DPDK 的遙測功能允許收集應用程序運行時的各種指標和事件,以便進行性能分析和優化。要使用遙測功能,需要執行以下步驟:
-
配置遙測,在應用程序啟動時,DPDK 會自動啟用遙測功能。可以通過環境變量或配置文件來調整遙測的行為,例如指定輸出的目標(如文件或套接字)、采樣頻率等。
-
編寫遙測點,在 DPDK 應用程序代碼中,可以使用 DPDK 提供的 API 函數來記錄遙測點。
例如使用
rte_telemetry_register_callback()
函數注冊一個回調函數,當特定的遙測事件發生時,該函數將被調用。 -
運行應用程序,啟動 DPDK 應用程序,遙測功能將在后臺收集指標和事件。
-
收集和分析遙測數據,DPDK 提供了工具來幫助收集和可視化遙測數據。例如,
dpdk-telemetry-client
工具可以連接到正在運行的 DPDK 應用程序,并實時顯示遙測指標。還可以將遙測數據導出到文件或其他工具進行進一步分析。
Once Day
也信美人終作土,不堪幽夢太匆匆......
如果這篇文章為您帶來了幫助或啟發,不妨點個贊👍和關注,再加上一個小小的收藏?!
(。???。)感謝您的閱讀與支持~~~