0%

周谈(39)-近期小事

前言

国庆后这周连续上来7天班,事情做了许多,也包含了许多小事儿,大的部分暂时没有准备好。本周就拼凑着讲一下几个小事儿。

DPDK ring出队是否卡住

节前的时候,组里的同事检视我的代码,提了一个意见。说是rte_ring这个环状队列,被调用批量出队的时候,如果对内里面的个数小于期望的个数,那么会返回0或者卡住。我就写了个demo验证了一下,结果如下:

rte_ring

可以看出,出队时并不会卡住,即使不满足期望个数也会出队实际的个数。

动态库的constructor方法在库被加载时才会被调用

我们知道,如果设置了一个函数的gcc属性为__attribute__(constructor),那么在main函数运行之前就会调用这个函数。

同事在把DPDK的driver库编译为动态库的时候,发现运行的时候很多数据没有正常初始化。然后打印了断点,函数也没有被断到,可以确定对应的函数没有执行。

想来没有加载动态库的时候,这些函数也就在main运行之前不会被调用了,很正常。DPDK的eal参数提供了加载指定驱动的命令选项, 可以通过加载指定的so,后续在运行的时候发现数据正常初始化了。

看了一下这块的代码,

1
2
3
-d LIB.so|DIR       Add a driver or driver directory
(can be used multiple times)

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
64
65
66
67
68
int
eal_parse_common_option(int opt, const char *optarg,
struct internal_config *conf)

...

/* force loading of external driver */
case 'd':
if (eal_plugin_add(optarg) == -1)
return -1;
break;

static int
eal_plugin_add(const char *path)
{
struct shared_driver *solib;

solib = malloc(sizeof(*solib));
if (solib == NULL) {
RTE_LOG(ERR, EAL, "malloc(solib) failed\n");
return -1;
}
memset(solib, 0, sizeof(*solib));
strlcpy(solib->name, path, PATH_MAX);
TAILQ_INSERT_TAIL(&solib_list, solib, next);

return 0;
}


int
eal_plugins_init(void)
{
struct shared_driver *solib = NULL;
struct stat sb;

/* If we are not statically linked, add default driver loading
* path if it exists as a directory.
* (Using dlopen with NOLOAD flag on EAL, will return NULL if the EAL
* shared library is not already loaded i.e. it's statically linked.)
*/
if (is_shared_build() &&
*default_solib_dir != '\0' &&
stat(default_solib_dir, &sb) == 0 &&
S_ISDIR(sb.st_mode))
eal_plugin_add(default_solib_dir);

TAILQ_FOREACH(solib, &solib_list, next) {

if (stat(solib->name, &sb) == 0 && S_ISDIR(sb.st_mode)) {
if (eal_plugindir_init(solib->name) == -1) {
RTE_LOG(ERR, EAL,
"Cannot init plugin directory %s\n",
solib->name);
return -1;
}
} else {
RTE_LOG(DEBUG, EAL, "open shared lib %s\n",
solib->name);
solib->lib_handle = eal_dlopen(solib->name);
if (solib->lib_handle == NULL)
return -1;
}

}
return 0;
}

可以看到,在参数解析的时候会把driver对应的路径存起来, 在后面的eal_plugins_init时候调用dlopen加载该动态库。
很多插件的实现原理也是类似,通过加载固定文件夹下的so,触发特定的函数执行,或者是调用so内部的特定函数,实现对应的功能。

destructor在函数异常退出是不会调用被调用

最近在设计一个用户态库的接口,需要对外提供资源的申请释放的接口,资源是通过ioctl调用内核的驱动模块来操作的,也就是多个用户态进程会共同使用内核的资源。我本来是想着,如果使用接口的进程异常退出了,那么就由用户态的库自动释放这些资源。本来我是想着使用attribute(destructor)属性的函数来实现已申请的资源的释放的。实验了一下,发现此路不同。

show you code:

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

#include <stdio.h>
#include <stdlib.h>

/*
用于验证异常情况下, destructor是否会被调用。

结论:
进程异常(被kill)退出,不会调用destructor函数。

*/
__attribute__((constructor)) static void beforeFunction()
{
printf("beforeFunction\n");
}

__attribute__((destructor)) static void afterFunction()
{
printf("afterFunction\n");
}

void main(void)
{
int i = 0;
int times = 60;
while (i++ < times)
{
printf("current i = %d\r\n", i);
sleep(1);
}

return;
}


编译运行之后,我使用kill -9 命令杀死这个进程,可以看到,并没有输出afterFunction。

destructor

device_create创建设备文件

之前在学习驱动的时候,文章字符设备驱动使用的mknod命令手动创建了设备文件。这周看了之前的驱动代码, 使用device_create函数创建了设备文件。

在调用device_create前要先用class_create创建一个类。类这个概念在Linux中被抽象成一种设备的集合。类在/sys/class目录中。

1
2
3
4
5
6
7
8
root@keep-VirtualBox:/sys/class# ls /sys/class/
ata_device block devlink drm_dp_aux_dev hidraw intel_scu_ipc mem net power_supply ptp remoteproc scsi_generic thermal vc watchdog
ata_link bsg dma extcon hwmon iommu misc pci_bus ppdev pwm rfkill scsi_host tpm vfio wwan
ata_port devcoredump dma_heap firmware i2c-adapter leds mmc_host pci_epc ppp rapidio_port rtc sound tpmrm virtio-ports
backlight devfreq dmi gpio i2c-dev lirc msr phy pps rc scsi_device spi_master tty vtconsole
bdi devfreq-event drm graphics input mdio_bus nd powercap printer regulator scsi_disk spi_slave usb_role wakeup
root@keep-VirtualBox:/sys/class#

主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   ...
globalmem_devp->dev_class = class_create(THIS_MODULE, "driver_dev");
if (IS_ERR(globalmem_devp->dev_class)) {
printk("class create error\r\n");
ret = -EBUSY;
goto fail_malloc;
}

devices = device_create(globalmem_devp->dev_class, NULL, MKDEV(globalmem_major, 0), NULL, "globalmem");
if (NULL == devices){
printk("device_create error\r\n");
ret = -EBUSY;
class_destroy(globalmem_devp->dev_class);
goto fail_malloc;
}
...

安装之后, 可以在/sys/class和/dev下找到driver_dev和globalmem

char_dev

更多

这篇知识比较零碎。具体的测试代码在Gitee仓库中git@gitee.com:fishmwei/blog_code.git。


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。

博客地址: https://fishmwei.github.io

掘金主页: https://juejin.cn/user/2084329776486919