dpdkDPDK

安装使用DPDK

2016-09-15  本文已影响4910人  Jale

mTCP中使用DPDK将网卡上的数据提取到用户态,然后经过用户态的TCP/IP协议进行处理。DPDK全称Data Plane Development Kit,是Intel发布的用于快速处理数据包的开发平台和接口1

网卡驱动在接收到数据包会产生中断来通知CPU进行处理,然后CPU拷贝数据给内核协议栈进行处理。当数据量非常大时,中断和数据拷贝的开销很大。DPDK将网卡的数据旁路到用户态直接进行处理,不经过内核,不产生中断也不进行昂贵的数据拷贝。Intel自己给出的性能数据是,一个数据包可以在80个时钟周期内处理完成,而正常情况下处理器访问DDR3内存都需要200个时钟周期,还不算协议处理的时间。

下载安装

系统要求

DPDK的文档中对Linux系统的部署有很详细的描述Guide for Linux

首先由于DPDK的是Intel推出的技术,因此只支持x86架构的机器。

查看是否支持hpet,如果不支持则无输出内容,需要在BIOS中开启:

grep hpet /proc/timer_list

Clock Event Device: hpet
 set_next_event: hpet_legacy_next_event
 shutdown: hpet_legacy_shutdown
 periodic: hpet_legacy_set_periodic
 oneshot:  hpet_legacy_set_oneshot
 resume:   hpet_legacy_resume

启动hugepage支持(64位系统推荐使用1G的hugepages):

# 每块内存大小2MB,共预留1024个2MB内存块
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

然后将hugepages中的内存给DPDK使用:

mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge
#add item to the /etc/fstab
nodev /mnt/huge hugetlbfs defaults 0 0

DPDK的源码下载和文档都在官网。下载最新版本的代码:

  wget http://fast.dpdk.org/rel/dpdk-16.07.tar.xz

解压后编译:

make install T=x86_64-native-linuxapp-gcc

其中T表示Target, Target的描述格式是:

ARCH-MACHINE-EXECENV-TOOLCHAIN
- ARCH = i686, x86_64, ppc_64
- MACHINE = native, ivshmem, power8
- EXECENV = linuxapp, bsdapp
- TOOLCHAIN = gcc, icc

编译完成之后生产了环境目录x86_64-native-linuxapp-gcc

cd x86_64-native-linuxapp-gcc
ls
app  build  include  kmod  lib  Makefile

加载uio内核模块:

modprobe uio_pci_generic

要使用DPDK,必须将网卡绑定到uio_pci_generi模块,DPDK在tools目录下提供了dpdk-devbind.py脚本完成这个工作。首先在绑定前查看一下状态:

[root@localhost tools]$ ./dpdk-devbind.py --status

Network devices using DPDK-compatible driver
============================================
<none>
Network devices using kernel driver
===================================
0000:00:03.0 '82540EM Gigabit Ethernet Controller' if=enp0s3 drv=e1000 unused= *Active*
0000:00:08.0 '82540EM Gigabit Ethernet Controller' if=enp0s8 drv=e1000 unused= *Active*

Other network devices
=====================
<none>

可以看到现在没有绑定到DPDK的网络接口,现在将enp0s8绑定到DPDK:

#需要先down掉,不然没办法绑定成功
[root@localhost tools]# ifconfig enp0s8 down
[root@localhost tools]# ./dpdk-devbind.py --bind=uio_pci_generic enp0s8
[root@localhost tools]# ./dpdk-devbind.py --status
Network devices using DPDK-compatible driver
============================================
0000:00:08.0 '82540EM Gigabit Ethernet Controller' drv=uio_pci_generic unused=e1000

Network devices using kernel driver
===================================
0000:00:03.0 '82540EM Gigabit Ethernet Controller' if=enp0s3 drv=e1000 unused=uio_pci_generic *Active*

Other network devices
=====================
<none>

可以看出,现在已经有一个设备使用DPDK了。

编译一个使用DPDK的简单程序

在编译之前必须将RET_SDKRTE_TARGET导入到环境变量中,其中RET_SDK是DPDK的安装目录,RTE_TARGET是DPDK目标环境目录。

export RTE_SDK=/home/lyt/dpdk-16.07
export RTE_TARGET=x86_64-native-linuxapp-gcc

在examples目录里面编译一个简单的应用:

cd examples/helloworld/
make

可以看到当前目录下生成了build目录,可执行程序就在build目录中,执行helloworld:

  [root@localhost build]# ./helloworld
  EAL: Detected 2 lcore(s)
  EAL: Probing VFIO support...
  PMD: bnxt_rte_pmd_init() called for (null)
  EAL: PCI device 0000:00:03.0 on NUMA socket -1
  EAL:   probe driver: 8086:100e rte_em_pmd
  EAL: PCI device 0000:00:08.0 on NUMA socket -1
  EAL:   probe driver: 8086:100e rte_em_pmd
  hello from core 1
  hello from core 0

hello world的代码结构

helloword程序成功编译并运行,来简单看一看代码:

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/queue.h>

#include <rte_memory.h>
#include <rte_memzone.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_debug.h>

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;
}

这个hello world代码非常简单,简单进行一下阐述。

  1. 第一步是调用rte_eal_init初始化EAL(Environment Abstraction Layer, 环境抽象层)。EAL在每一个slave核上都创建一个线程,并绑定CPU。
  2. 使用RTE_LCORE_FOREACH_SLAVE遍历分配给DPDK的slave CPU核心,然后调用rte_eal_mp_remote_launch注册回调函数lcore_hello函数打印。
  3. 在master lcore上调用lcore_hello。
  4. rte_eal_mp_wait_lcore等待所有的slave核心退出,然后自己再退出。
上一篇下一篇

猜你喜欢

热点阅读