接续前节:
程序逻辑:
运行参数:
关键API:
入口函数:
内存池函数:
。 它是函数 的一个封装。
struct rte_mempool * rte_pktmbuf_pool_create(const char *name, unsigned n, unsigned cache_size, uint16_t priv_size, uint16_t data_room_size, int socket_id)
name是内存池名字。为了获得更好的性能,n应该为2的幂减1 。
网卡操作函数:
设置网卡设备。在其他操作之前,应该先调用这个函数进行设置。
申请并设置一个收包队列。
关键参数:
struct rte_mempool *mp; 由前文创建的pool
rte_eth_tx_queue_setup() 同上。
就是设置好了之后就启动啊,该收的收,该发的发。
启动混杂模式,不解释。
收发包函数:
收一大批包
该接口不提供任何错误检测功能,上层应用可以在返回包数为零时,去主动检测link状态来完成接口异常及错误检测机制。
关键参数:
struct rte_mbuf** rx_pkts; 一个指针数组,数组中的每一个指针指向收取到的一个包,具体的包结构查看下文的数据结构章节。指针所指向的内存空间为queue_setup(mpool) 函数中的参数pool提供。
const uint16_t nb_pkts; 简单来说,就是数组大小。
返回值:
收到的报数,数组中被填充的item个数。
当返回值== nb_pkts时,隐含说明,收包性能已经跟不上了。
当返回值== 0 时,应该启动异常检测,查看接口状态等。
发一大批包
收到了但是没有被发出去的包,应该将其free,即还给mpool。咦,不过为什么会有没发出去的呢?奇怪
其他函数:
rte_eth_dev_count():
返回可以被dpdk使用的网口个数。即(加载了UIO驱动,或VFIO ??) rte_eal_init 之后就可以用了。
rte_socket_id():
返回CPU sock 的ID,即命令参数中指定的lcore所属的CPU socket。
回过头来,对比一下Helloworld。在helloworld里多使用了一个函数
rte_eal_remote_launch() 用于在多个核上启动多线程,原例子中用法如下:
/* 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); lcore_id = rte_lcore_id(); printf("hello from master core %u\n", lcore_id);
主线程,跑着编号最小的那个核上,不知道是否可修改。
[root@dpdk ~]# ps -eLF |grep -E "UID|helloworld"UID PID PPID LWP C NLWP SZ RSS PSR STIME TTY TIME CMDroot 2671 2531 2671 99 5 274512 3668 4 18:44 pts/0 00:05:22 ./helloworld -l4,5,6,7root 2671 2531 2672 0 5 274512 3668 4 18:44 pts/0 00:00:00 ./helloworld -l4,5,6,7root 2671 2531 2673 0 5 274512 3668 5 18:44 pts/0 00:00:00 ./helloworld -l4,5,6,7root 2671 2531 2674 0 5 274512 3668 6 18:44 pts/0 00:00:00 ./helloworld -l4,5,6,7root 2671 2531 2675 0 5 274512 3668 7 18:44 pts/0 00:00:00 ./helloworld -l4,5,6,7root 2778 2679 2778 0 1 28162 964 0 18:50 pts/1 00:00:00 grep --color=auto -E UID|helloworld[root@dpdk ~]#
数据结构:
struct rte_mbuf {}
gdb之: 去掉 -O3
设断点:
(gdb) info bNum Type Disp Enb Address What1 breakpoint keep y 0x0000000000435829 in lcore_main at /root/src/sdk/@dpdk/dpdk-stable-16.07.1/examples/skeleton/basicfwd.c:143 breakpoint already hit 1 time(gdb) l 143138 for (;;) {139 /*140 * Receive packets on a port and forward them on the paired141 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.142 */143 for (port = 0; port < nb_ports; port++) {144145 /* Get burst of RX packets, from first port of pair. */146 struct rte_mbuf *bufs[BURST_SIZE];147 const uint16_t nb_rx = rte_eth_rx_burst(port, 0,(gdb)
debug一个包:
包格式如下:
进入断点,看数据结构,如下:
(gdb) p bufs[0]$20 = (struct rte_mbuf *) 0x7fffd9791b00(gdb) p *bufs[0]$21 = {cacheline0 = 0x7fffd9791b00, buf_addr = 0x7fffd9791b80, buf_physaddr = 972626816, buf_len = 2176, rearm_data = 0x7fffd9791b12 "\200", data_off = 128, { refcnt_atomic = {cnt = 1}, refcnt = 1}, nb_segs = 1 '\001', port = 0 '\000', ol_flags = 0, rx_descriptor_fields1 = 0x7fffd9791b20, {packet_type = 0, { l2_type = 0, l3_type = 0, l4_type = 0, tun_type = 0, inner_l2_type = 0, inner_l3_type = 0, inner_l4_type = 0}}, pkt_len = 42, data_len = 42, vlan_tci = 0, hash = {rss = 0, fdir = { { {hash = 0, id = 0}, lo = 0}, hi = 0}, sched = {lo = 0, hi = 0}, usr = 0}, seqn = 0, vlan_tci_outer = 0, cacheline1 = 0x7fffd9791b40, { userdata = 0x0, udata64 = 0}, pool = 0x7fffd64436c0, next = 0x0, {tx_offload = 0, {l2_len = 0, l3_len = 0, l4_len = 0, tso_segsz = 0, outer_l3_len = 0, outer_l2_len = 0}}, priv_size = 0, timesync = 0}(gdb) x/42xb (bufs[0].buf_addr + 128)0x7fffd9791c00: 0xff 0xff 0xff 0xff 0xff 0xff 0x00 0x000x7fffd9791c08: 0x00 0x01 0x00 0x01 0x08 0x06 0x00 0x010x7fffd9791c10: 0x08 0x00 0x06 0x04 0x00 0x01 0x00 0x000x7fffd9791c18: 0x00 0x01 0x00 0x01 0x01 0x01 0x01 0x010x7fffd9791c20: 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x010x7fffd9791c28: 0x01 0x02(gdb) where
回调函数和CPU cycle
例子 在 skeleton 的基础之上增加了两个回调函数,在回调函数中做了cpu cycles的计算。
rte_eth_add_tx_callback()
关于CPU cycles可以参见另一篇博