Intel开源的x86平台上的报文处理套件。
要求
Kernel >= 2.6.33
glibc >= 2.7
Kernel需要支持UIO、HUGETLBFS、PROC_PAGE_MONITOR
如果使用HPET支持, Kernel需要支持HPET和HPET_MMAP
如果使用HPET timer和电源管理(power management)功能, 需要设置BIOS
HUGETLBFS是指通过使用大的内存页, 减少TLB条目的数量, 提高TLB缓存的命中率, 提高了内存地址转换的效率,从而提高了内存的操作效率。
dpdk中用来存放报文的内存使用的就是HUGEPAGE
安装
下载源码解压。按照doc/build-sdk-quick.txt指示编译,安装。
make config T=x86_64-default-linuxapp-gcc DESTDIR=/opt/dpdk/
make //编译过程中发现有个别结构体重复定义,注释掉就可以了
make install T=x86_64-default-linuxapp-gcc DESTDIR=/opt/dpdk/
在另一个目录中编译:
make config T=x86_64-default-linuxapp-gcc O=my_sdk_build_dir
然后在my_sdk_build_dir中直接make, 就在新的目录中完成了dpdk的编译
安装后在安装目录下生成两个目录:
mk //dpdk开发框架的的全套makefile
x86_64-default-linuxapp-gcc //dpdk的头文件和对应架构下的链接库
加载驱动:
到安装目录下找到kmod/igb_uio.ko kmod/rte_kni.ko
modprobe uio
insmod igb_uio.ko
运行前准备
在运行dpdk应用时需要准备的事项。
设置hugepages
系统启动前,通过内核参数分配内存:
// 分配1024个2M的页
hugepages=1024
// 分配4个1G的页, 64位应用推荐使用1G的页
default_hugepages=1G hugepagesz=1G hugepages=4
在系统运行的情况,通过修改/proc中参数分配:
// 分配24个2MB的页
echo 24 >/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
在系统运行时,直接mount挂载hugepages:
mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge
或者添加到添加到/etc/fstab中,启动时自动挂载:
nodev /mnt/huge hugetlbfs defaults 0 0
1GB的页需要指定页的大小
nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0
加载内核模块
dpdk依赖uio, 提供了igb_uio.ko, 需要将这两个内核模块加载:
sudo modprobe uio
sudo insmod kmod/igb_uio.ko
在dpdk编译后的目录中可以看到dpdk提供的驱动:
[root@localhost kmod]# ls
igb_uio.ko rte_kni.ko
替换网卡的驱动
在dpdk对网卡的称呼是port, 就是网口。
dpdk的应用程序要使用某块网卡是,需要使用tools/pci_unbind.py将网卡绑定到驱动igb_uio。
依赖lspci命令
使用pci_unbind.py查看网卡状态:
[root@localhost tools]# ./pci_unbind.py –status //查看网卡状态
Network devices using IGB_UIO driver
====================================
<none>
Network devices using kernel driver
===================================
0000:02:01.0 '82545EM Gigabit Ethernet Controller (Copper)' if=eth1 drv=e1000 unused=igb_uio *Active*
0000:02:05.0 '82545EM Gigabit Ethernet Controller (Copper)' if=eth2 drv=e1000 unused=igb_uio
Other network devices
=====================
<none>
设置eth2使用igb_uio:
[root@localhost tools]# ./pci_unbind.py –bind=igb_uio eth2 //eth2绑定到驱动igb_uio
查看绑定后的网卡状态:
[root@localhost tools]# ./pci_unbind.py –status //绑定后的网卡状态
Network devices using IGB_UIO driver
====================================
0000:02:05.0 '82545EM Gigabit Ethernet Controller (Copper)' drv=igb_uio unused=e1000
Network devices using kernel driver
===================================
0000:02:01.0 '82545EM Gigabit Ethernet Controller (Copper)' if=eth1 drv=e1000 unused=igb_uio *Active*
Other network devices
=====================
<none>
自动化工具
dpdk中提供了一个脚本(tools/setup.sh), 用于方便的编译以及设置运行环境.
[root@localhost dpdk-1.5.0r1]# source tools/setup.sh
RTE_SDK exported as /export/App/ads/dpdk/dpdk-1.5.0r1
Step 1: Select the DPDK environment to build
[1] i686-default-linuxapp-gcc
[2] i686-default-linuxapp-icc
[3] x86_64-default-linuxapp-gcc
[4] x86_64-default-linuxapp-icc
Step 2: Setup linuxapp environment
[5] Insert IGB UIO module
[6] Insert KNI module
[7] Setup hugepage mappings for non-NUMA systems
[8] Setup hugepage mappings for NUMA systems
[9] Display current Ethernet device settings
[10] Bind Ethernet device to IGB UIO module
…
使用其它特性
HPET – High Precision Event Timer
HPET需要BIOS支持:
Advanced->PCH-IO Configuration -> High Precision Timer
可以从/proc/timer_list中查看HPET是否已经开启:
grep hpet /proc/timer_list
内核选项需要勾选HPET_MMAP
在Fedora、Ubuntu等发行版中, HPET_MMAP默认是disable的, 需要重新编译内核。
dpdk默认是不使用HPET的,因此还需要在dpdk的配置文件(源码config目录下)中设置支持HPET:
CONFIG_RTE_LIBEAL_USE_HPET=n
使用非Root身份运行
dpdk可以使用非root身份运行,需要设置好一下的目录或文件的所属:
hugepage挂在点:
/mnt/huge
userspace-io设备文件:
/dev/uio0 …
如果使用HPET:
/dev/hpet
使用电源管理功能
需要在BIOS中启用Enhanced Intel SpeedStep Technology
Advanced->Processor Configuration->Enhanced Intel SpeedStep® Tech
还需要在BIOS中启用C3、C6
Advanced->Processor Configuration->Processor C3
Advanced->Processor Configuration-> Processor C6
core isolation – 独占CPU核心
将dpdk应用绑定到特定的CPU上后, 这些CPU还是参与系统调度,承担系统的其它的任务。
可以通过配置Linux的内核参数使dpdk占用的CPU不参与调度,从而使dpdk应用独占CPU。
isolcpus=2,4,6 //核2, 4, 6不参与任务调度
Kernel NIC Interface
dpdk的示例程序中给出了一个KNI(Kernel NIC Interface)示例, 要使用该示例, 需要加载kni模块:
insmod kmod/rte_kni.ko
这个模块和igb_uio.ko在一个目录,前面已经见过。
使用利用Intel VT-d的IOMMU Pass-Through
TODO: 对IOMMU不了解,这里是猜测
经查Passthrough I/O模型是指在客户机内部能够直接对硬件进行操作, Intel的支持技术叫做VT-d(Virtualization Technology for Directed I/O),AMD的支持技术叫做IOMMU(I/O Memory Management Unit)。这里涉及的应当是dpdk应用运行在虚拟机中的情形, 在虚拟机中的dpdk应用需要能够透过虚拟机直接访问设备硬件。
首先要启用Intel VT技术, 需要设置内核的编译选项:
IOMMU_SUPPORT
IOMMU_API
INTEL_IOMMU
运行时需要指定内核参数:
iommu = pt
如果编译的内核中没有设置INTEL_IOMMU_DEFAULT_ON, 还需要指定内核参数:
intel_iommu=on
TODO:这一节的内容还需要继续深挖, 特别是虚拟化相关的内容。
应用运行
dpdk提供了一个统一的环境抽象层(EAL, Environmental Abstraction Layer), 这一层为dpdk应用提供了通用的统一的选项。
./rte-app -c COREMASK -n NUM [-b
binary name
APP = helloworld #指定应用名称,编译得到的程序将会使用这个名字
all source are stored in SRCS-y
SRCS-y := main.c #指定源文件
CFLAGS += -O3 #编译选项
CFLAGS +=
(WERRORFLAGS)include
(RTE_SDK)/mk/DPDK.extapp.mk
main.c:
include “main.h”
/******* lcore_hello是每个执行单元的工作内容 ********/
static int
lcore_hello(attribute((unused)) void *arg)
{
| unsigned lcore_id;
| lcore_id = rte_lcore_id();
| printf(“hello from core %u\n”, lcore_id);
| return 0;
}
int
MAIN(int argc, char **argv)
{
| int ret;
| unsigned lcore_id;
| ret = rte_eal_init(argc, argv);
| if (ret < 0)
| | rte_panic(“Cannot init EAL\n”);
| /* 将工作内容下发到其它的执行单元 */
| /* call lcore_hello() on every slave lcore */
| RTE_LCORE_FOREACH_SLAVE(lcore_id) {
| | rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
| }
| /* 当前执行单元的工作内容 */
| /* call it on master lcore too */
| lcore_hello(NULL);
| /* 等待所有的执行单元结束 */
| rte_eal_mp_wait_lcore();
| return 0;
}
编译:
make O=pwd
/../out
O=指定输出路径, 如果不指定默认是当前路径下的build目录
编译结果:
[root@localhost build]# ls -R
.:
app helloworld helloworld.map _install main.o _postbuild _postinstall _preinstall
./app:
helloworld helloworld.map
运行:
echo 24 >/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge
sudo modprobe uio
sudo insmod kmod/igb_uio.ko
./helloworld -c 3 -n 1
运行结果:
[root@localhost build]# ./helloworld -c 7 -n 1
EAL: Detected lcore 0 as core 0 on socket 0 //CPU核心检测
EAL: Detected lcore 1 as core 1 on socket 0
EAL: Detected lcore 2 as core 2 on socket 0
EAL: Skip lcore 3 (not detected)
…
EAL: Skip lcore 63 (not detected)
EAL: Setting up memory…
EAL: Ask a virtual area of 0x2097152 bytes //TODO!!!
EAL: Virtual area found at 0x7f056fe00000 (size = 0x200000)
EAL: Ask a virtual area of 0x2097152 bytes
EAL: Virtual area found at 0x7f056fa00000 (size = 0x200000)
EAL: Ask a virtual area of 0x12582912 bytes
EAL: Virtual area found at 0x7f056ec00000 (size = 0xc00000)
EAL: Ask a virtual area of 0x2097152 bytes
EAL: Virtual area found at 0x7f056e800000 (size = 0x200000)
EAL: Ask a virtual area of 0x2097152 bytes
EAL: Virtual area found at 0x7f056e400000 (size = 0x200000)
EAL: Requesting 10 pages of size 2MB from socket 0
EAL: TSC frequency is ~2459655 KHz //TODO!!!
EAL: WARNING: cpu flags constant_tsc=yes nonstop_tsc=no -> using unreliable clock cycles ! //TODO!!!
EAL: Master core 0 is ready (tid=7103a840)
EAL: Core 2 is ready (tid=6d3fd700)
EAL: Core 1 is ready (tid=6dbfe700)
hello from core 1 //执行单元的工作结果
hello from core 0
hello from core 2
从运行结果的输出可以看到,启动时首先会检测设备的CPU核心数(检测0-63,如果存在就显示Core的位置), 然后申请内存等, Core就位后开始执行单元的执行。
API
首先到dpdk的安装目录下查看一下dpdk提供的内容:
[root@localhost dpdk-1.5]# ls
mk x86_64-default-linuxapp-gcc
其中mk目录下dpdk提供的一套makefile, 在这些makefile的辅助下, 项目的顶层Makefile变得非常整洁。
x86_64-default-linuxapp-gcc对应平台上的开发接口。
[root@localhost x86_64-default-linuxapp-gcc]# ls
app include kmod lib
app: 编译得到dpdk的应用程序
include: dpdk对外提供的接口文件(.h文件)
kmod: dpdk的相关内核模块
lib: dpdk的静态库文件(.a)
这里关心的是include目录中的.h文件中都提供了哪些接口, 分别作什么用途。intel-dpdk-programmers-guide.pdf做了分类说明。
详细内容可以查看DPDK在线API手册
常用接口
dpdk提供了对网卡进行操作的接口, 关于网卡的知识可以从这里获取Intel官网上获取。
Intel网络适配器列表
Parameter Talk: TX and RX descriptors
rte_eal_init(int argc, char **argv)
//EAL的初始化,在master core上运行
rte_pmd_init_all
rte_eal_pci_probe
rte_lcore_count
rte_eth_dev_count
rte_eth_macaddr_get
rte_eth_dev_configure
//设置网卡的接收队列和发送队列数目, 以及硬件特性
rte_lcore_is_enabled
rte_lcore_to_socket_id
rte_mempool_create
rte_eth_tx_queue_setup
rte_eth_rx_queue_setup
rte_eth_dev_start
rte_eth_promiscuous_enable
rte_eth_link_get_nowait
rte_ring_create
rte_rand
rte_eal_mp_remote_launch
rte_lcore_id
rte_ring_dequeue_burst
rte_pktmbuf_mtod
rte_rdtsc
rte_eth_rx_burst
rte_prefetch0
rte_be_to_cpu_16
rte_jhash_2words
rte_ring_enqueue
RTE_PER_LCORE
优化
《intel-dpdk-programmers-guide.pdf》给出了一些性能优化建议
内存
在data plane操作内存的时候, 使用dpdk的api, 不要使用libc
例如: rte_memcpy – memcpy rte_malloc – malloc
降低cache miss
通过使用RTE_PER_LCORE
NUMA
均衡Memory Channels
核间通信
使用dpdk ring进行核间通信
Poll Mode Driver (PMD)
低延迟与高吞吐之间的延迟
控制每次轮询网卡时处理的报文的数量
锁和原子操作
在data plane避免使用锁
使用per-lcore variables 使用RCU算法替代rwlocks
编码技巧
使用inline函数
使用分支预测
if (likely(x>1)) XXXX;
设置CPU类型
dpdk支持对特定的CPU架构进行优化
通过CONFIG_RTE_MACHINE指定CPU特性
对虚拟化的支持
DPDK对虚拟化提供了比较全面的支持。可以用于虚拟机中。
对比pf_ring
直观感觉,dpdk的定位是sdk, 提供了更多的支持库, 自成体系, 绑定在intel平台上。pf_ring定位是高效的抓包工具, 支持的硬件更多一些。
文献
intel-dpdk-programmers-guide.pdf
intel-dpdk-getting-started-guide.pdf
http://www.lijiaocn.com/blog/2014/03/28/DPDK.html