学习使用dpdk

了解一下dpdk. 它实现了一系列的驱动和库, 可以帮助来进行网络数据包的采集, 具体可以用来干什么呢? 引用一下官网的话:

These libraries can be used to:

  • receive and send packets within the minimum number of CPU cycles (usually less than 80 cycles)
  • develop fast packet capture algorithms (tcpdump-like)
  • run third-party fast path stacks

报着学习的态度, 这里从头开始安装使用一下. 网上相关的资料还是不少的, 这里稍微总结一下. 相关的软件版本是debian 7.8dpdk 1.8.0.

安装

主要的步骤还是参考官网的quick-start, 在安装前还是需要确认几点:

一旦确定ok, 下来就是简单的几步:

  1. 下载源码
  2. 执行make install T=x86_64-native-linuxapp-gcc, 这一步会在源码目录生成对应的x86_64-native-linuxapp-gcc目录.

跑 Hello World

跑相关的应用直接需要先做以下几步:

1.加载uio以及igb_uio(对应安装目录下面)

2.绑网卡(使用tools/dpdk_nic_bind.py), 注意这里需要root权限. 我这里是虚拟了2快网卡:

bind
1
2
# 这里的ethX也可以换成数字(--status可以看到)
sudo tools/dpdk_nic_bind.py --bind=igb_uio ethX

3.有关内存的设置:

huge pages
1
2
3
sudo mkdir -p /mnt/huge
sudo mount -t hugetlbfs nodev /mnt/huge
sudo echo 64 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages

4.设定环境变量:

env
1
2
3
# 也可以写到一个文件里面然后source进来
export RTE_SDK=/YOUR/DPDK/SOURCE/PATH/dpdk-1.8.0
export RTE_TARGET=x86_64-native-linuxapp-gcc

5.进入examples/helloworld编译, 然后执行:

exec
1
sudo build/helloworld -c 3 -n 1

之后就可以看到对应的输出了(我这边有两个cpu):

output
1
2
hello from core 1
hello from core 0

详细的安装和使用可以参考intel这篇

代码

说实话, 之前看了文档执行到这还是有点不知道究竟是怎么回事, 就例如启动参数那个-c-n. -c是指cpu的mask, 表示使用那几个cpu. 这里顺着helloworld代码简单看一下. 所有应用启动时都会调用rte_eal_init这个函数, 用于初始化一些全局变量, 工作线程以及解析参数, -c参数的解析也在其中:

lib/librte_eal/common/eal_common_options.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
static int
eal_parse_coremask(const char *coremask)    // coremask是-c后面的值
{
    struct rte_config *cfg = rte_eal_get_configuration();
    int i, j, idx = 0;
    unsigned count = 0;
    char c;
    int val;

    if (coremask == NULL)
        return -1;
    /* Remove all blank characters ahead and after .
     * Remove 0x/0X if exists.
     */
    while (isblank(*coremask))
        coremask++;
    if (coremask[0] == '0' && ((coremask[1] == 'x')
        || (coremask[1] == 'X')))
        coremask += 2;
    i = strnlen(coremask, PATH_MAX);
    while ((i > 0) && isblank(coremask[i - 1]))
        i--;
    if (i == 0)
        return -1;

    for (i = i - 1; i >= 0 && idx < RTE_MAX_LCORE; i--) {
        c = coremask[i];
        if (isxdigit(c) == 0) {
            /* invalid characters */
            return -1;
        }
        val = xdigit2val(c);
        for (j = 0; j < BITS_PER_HEX && idx < RTE_MAX_LCORE; j++, idx++)
        {
            if ((1 << j) & val) {
                if (!lcore_config[idx].detected) {        //关键
                    RTE_LOG(ERR, EAL, "lcore %u "
                            "unavailable\n", idx);
                    return -1;
                }
                cfg->lcore_role[idx] = ROLE_RTE;
                lcore_config[idx].core_index = count;
                count++;
            } else {
                cfg->lcore_role[idx] = ROLE_OFF;
                lcore_config[idx].core_index = -1;
            }
        }
    }
    for (; i >= 0; i--)
        if (coremask[i] != '0')
            return -1;
    for (; idx < RTE_MAX_LCORE; idx++) {
        cfg->lcore_role[idx] = ROLE_OFF;
        lcore_config[idx].core_index = -1;
    }
    if (count == 0)
        return -1;
    /* Update the count of enabled logical cores of the EAL configuration */
    cfg->lcore_count = count;
    lcores_parsed = 1;
    return 0;
}

上面的关键步骤中cpu的探测也是在rte_eal_init中完成, 实现的函数:

lib/librte_eal/linuxapp/eal/eal_lcore.c
1
2
3
4
5
6
7
8
9
10
11
12
13
static int
cpu_detected(unsigned lcore_id)
{
    char path[PATH_MAX];
    int len = snprintf(path, sizeof(path), SYS_CPU_DIR
        "/"CORE_ID_FILE, lcore_id);
    if (len <= 0 || (unsigned)len >= sizeof(path))
        return 0;
    if (access(path, F_OK) != 0)
        return 0;

    return 1;
}

有关-n真的是不怎么了解, 这里有个同学写了相关的文章, 可以了解下.

其他

这里仅仅是跑了一个最简单的例子, 相关lib的使用可以继续参考其他例子官方文档. 对接收包转发包比较感兴趣的可以先阅读examples/distributor/main.c, 比较短, 也比较好读好理解.

继续学习~

Comments