DPDK

来源:互联网 发布:sql分页查询语句limit 编辑:程序博客网 时间:2024/05/19 13:27

摘要
http://www.dpdk.org/

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=e1000Network 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

0 0