DPDK代码中广泛使用RTE_INIT宏进行设备驱动或者RTE模块等的初始化工作,其核心是RTE_INIT_PRIO宏,定义在文件rte_common.h中。如下可见,RTE_INIT_PRIO宏的实现,实际为一个附带GCC编译属性的函数定义。此处用到两个属性,constructor和used。其中后一个used比较简单,向GCC编译器表明此函数的有用性,即使函数没有被任何地方引用,也不要警告。constructor构造属性,类似C++的构造函数,其标注的函数将在主函数(main)之前执行,相关代码位于目标文件的.ctors区(section)。
lib/librte_eal/common/include/rte_common.h
#define RTE_INIT_PRIO(func, prio) \
static void __attribute__((constructor(RTE_PRIO(prio)), used)) func(void)
另外RTE_PRIO宏指定了constructor的优先级,即函数执行的顺序,值越小优先级越高。目前定义了4个优先级:RTE_PRIORITY_LOG、RTE_PRIORITY_BUS、RTE_PRIORITY_CLASS和RTE_PRIORITY_LAST。
#define RTE_PRIORITY_LOG 101
#define RTE_PRIORITY_BUS 110
#define RTE_PRIORITY_CLASS 120
#define RTE_PRIORITY_LAST 65535
#define RTE_PRIO(prio) RTE_PRIORITY_ ## prio
其中RTE_INIT宏封装了RTE_PRIORITY_LAST优先级的函数。RTE_REGISTER_BUS宏封装了RTE_PRIORITY_BUS优先级的函数。而RTE_REGISTER_CLASS宏封装了RTE_PRIORITY_CLASS优先级的函数。目前DPDK中RTE_PRIORITY_LOG优先级的函数仅有一个rte_log_init,优先级最高,最先初始化(函数所在文件:lib/librte_eal/common/eal_common_log.c)。
#define RTE_INIT(func) RTE_INIT_PRIO(func, LAST)
lib/librte_eal/common/include/rte_bus.h
#define RTE_REGISTER_BUS(nm, bus) \
RTE_INIT_PRIO(businitfn_ ##nm, BUS) \
{\
(bus).name = RTE_STR(nm);\
rte_bus_register(&bus); \
}
lib/librte_eal/common/include/rte_class.h
#define RTE_REGISTER_CLASS(nm, cls) \
RTE_INIT_PRIO(classinitfn_ ##nm, CLASS) \
{\
(cls).name = RTE_STR(nm); \
rte_class_register(&cls); \
}
如下编译DPDK的examples,如vhost程序,查看生成的map文件(vhost-switch.map),检查constructor属性生成的初始化构造函数:
$ cd examples/vhost
$ export RTE_SDK=/home/k/network/dpdk-19.02
$ export RTE_TARGET=build
$ make
CC main.o
CC virtio_net.o
LD vhost-switch
INSTALL-APP vhost-switch
INSTALL-MAP vhost-switch.map
$
$ vi build/app/vhost-switch.map
如下所示,可见按照优先级顺序排列的初始化函数,优先级最低的RTE_PRIORITY_LAST未显示其数值。
.init_array 0x000000000099e400 0x670
0x000000000099e400 PROVIDE (__init_array_start, .)
*(SORT(.init_array.*) SORT(.ctors.*))
.init_array.00101
0x000000000099e400 0x8 /home/k/network/dpdk-19.02/build/lib/librte_eal.a(eal_common_log.o)
.init_array.00110
0x000000000099e408 0x8 /home/k/network/dpdk-19.02/build/lib/librte_bus_pci.a(pci_common.o)
.init_array.00110
0x000000000099e410 0x8 /home/k/network/dpdk-19.02/build/lib/librte_bus_vdev.a(vdev.o)
.init_array.00110
0x000000000099e418 0x8 /home/k/network/dpdk-19.02/build/lib/librte_bus_dpaa.a(dpaa_bus.o)
.init_array.00110
0x000000000099e420 0x8 /home/k/network/dpdk-19.02/build/lib/librte_bus_fslmc.a(fslmc_bus.o)
.init_array.00110
0x000000000099e428 0x8 /home/k/network/dpdk-19.02/build/lib/librte_bus_vmbus.a(vmbus_common.o)
.init_array.00110
0x000000000099e430 0x8 /home/k/network/dpdk-19.02/build/lib/librte_bus_ifpga.a(ifpga_bus.o)
.init_array.00120
0x000000000099e438 0x8 /home/k/network/dpdk-19.02/build/lib/librte_ethdev.a(rte_class_eth.o)
*(.init_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors)
.init_array 0x000000000099e440 0x8 /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
.init_array 0x000000000099e448 0x8 /home/k/network/dpdk-19.02/build/lib/librte_ip_frag.a(ip_frag_internal.o)
.init_array 0x000000000099e450 0x8 /home/k/network/dpdk-19.02/build/lib/librte_lpm.a(rte_lpm.o)
通过查看DPDK代码可见,各个函数在vhost-switch.map中的顺序与其在代码中的注册的优先级相符。
lib/librte_eal/common/eal_common_log.c RTE_INIT_PRIO(rte_log_init, LOG)
drivers/bus/pci/pci_common.c RTE_REGISTER_BUS(pci, rte_pci_bus.bus);
drivers/bus/vdev/vdev.c RTE_REGISTER_BUS(vdev, rte_vdev_bus);
drivers/bus/dpaa/dpaa_bus.c RTE_REGISTER_BUS(FSL_DPAA_BUS_NAME, rte_dpaa_bus.bus);
drivers/bus/fslmc/fslmc_bus.c RTE_REGISTER_BUS(FSLMC_BUS_NAME, rte_fslmc_bus.bus);
drivers/bus/vmbus/vmbus_common.c RTE_REGISTER_BUS(vmbus, rte_vmbus_bus.bus);
drivers/bus/ifpga/ifpga_bus.c RTE_REGISTER_BUS(IFPGA_BUS_NAME, rte_ifpga_bus);
lib/librte_ethdev/rte_class_eth.c RTE_REGISTER_CLASS(eth, rte_class_eth);
另外,DPDK中定义了RTE_FINI相关宏,其使用了GCC的destructor和used两个属性,used的意义与RTE_INIT中的相同。destructor属性表明函数在主函数(main)结束之后执行。不过目前,在DPDK代码中并没有地方使用RTE_FINI宏。
lib/librte_eal/common/include/rte_common.h
#define RTE_FINI_PRIO(func, prio) \
static void __attribute__((destructor(RTE_PRIO(prio)), used)) func(void)
#define RTE_FINI(func) RTE_FINI_PRIO(func, LAST)
lib/librte_eal/common/include/rte_class.h
#define RTE_UNREGISTER_CLASS(nm, cls) \
RTE_FINI_PRIO(classfinifn_ ##nm, CLASS) \
{ \
rte_class_unregister(&cls); \
}
DPDK版本dpdk-19.02。