工作中遇到的几个问题

121 阅读3分钟

前言

最近事情做了许多,也包含了许多小事儿,大的部分暂时没有准备好。本周就拼凑着讲一下几个小事儿。

DPDK ring出队是否卡住

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

rte_ring.png

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

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

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

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

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

看了一下这块的代码,

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

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:


#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.png

device_create创建设备文件

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

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

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#

主要代码如下:

    ...
    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.png


行动,才不会被动!

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

博客地址: fishmwei.github.io

掘金主页: juejin.cn/user/208432…