QEMU在main函数前对模块的初始化过程

初始化的难题

QEMU中包含了大量的初始化函数,比如使用QOM模型设计的很多类(CPU、设备等都是利用QOM模型设计实现模拟的),这些类需要注册到管理类型的全局的hash表中,这个注册的过程需要在初始化函数中完成。

想象一下,如果我们把这些注册过程都放到main函数里面调用,main函数中就会有非常长的一段篇幅,仅仅是用于调用大量的初始化函数,这样对于QEMU的代码维护非常不利,因此QEMU将这些初始化函数的指针保存到了链表数据结构中,这样只要遍历一遍链表就可以执行全部的初始化函数。说起来简单,但是其中的设计和实现,以及对GCC中相关特性的使用都有我们值得学习和研究的地方。
模块化管理初始化工作

QEMU中的代码的初始化管理是分模块的,实现这种模块化的代码文件包括include/qemu/module.h 和util/module.c。在include/qemu/module.h中所涉及的相关的代码模块包括:

    block (QEMU中的块操作实现代码)
    machine (QEMU中模拟设备的实现代码模块)
    qapi (QEMU向上层调用者提供的接口的代码模块)
    type 或者qom(QEMU中的QOM模型所涉及的代码模块)

重要的全局变量: init_type_list

该变量的定义如下:

//这个数据结构保存了某种类型的代码模块的一个初始化函数中指针
typedef struct ModuleEntry
{
    void (*init)(void);
    QTAILQ_ENTRY(ModuleEntry) node;
    module_init_type type;
} ModuleEntry;

typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;

//其中MODULE_INIT_MAX为4,也就是4种模块,每一种对应一个ModuleTypeList
//每个ModuleTypeList将保存一种代码模块中所有初始化函数指针
static ModuleTypeList init_type_list[MODULE_INIT_MAX];

qemu中的module实现了QEMU在main函数之前需要执行的大量函数
首先普及一下gcc的一些知识
gcc中的构造函数属性和析构函数属性

gcc为函数提供了几种类型的属性,其中就包括构造函数(contructors),用户可以定义一个函数:

static void __attribute__((constructor)) start(void)

这样start函数就会在main函数执行之前执行。

同理gcc中也有__attribute__((destructor)),会在main函数之后执行。
gcc中宏的##和#扩展符

在gcc的宏中,#扩展符可以将宏字符串化,##扩展符可以将它左右两边连接起来。比如:

#define SSVAR(X,Y) const char X[]=#Y  
SSVAR(InternetGatewayDevice, InternetGatewayDevice.); 

等价于如下代码:

const char InternetGatewayDevice[]="InternetGatewayDevice.";

又比如:

#define DEV_FILE_NAME    "/dev/test_kft"  

#define OPEN_FILE(fd, n) \  
{  \  
      fd = open(DEV_FILE_NAME ##n, 0); \  
   if (fd < 0) \  
   { \  
      printf("Open device error/n"); \  
      return 0; \  
   } \  
}  
OPEN_FILE(fd1, 1);  
OPEN_FILE(fd2, 2); 

它等价于:

{ fd1 = open(DEV_FILE_NAME1, 0); if (fd1 < 0) { printf("Open device error/n"); return 0; } };  
{ fd2 = open(DEV_FILE_NAME2, 0); if (fd2 < 0) { printf("Open device error/n"); return 0; } }; 

言归正传

要想看QEMU在main函数之前做了什么,可以直接查找__attribute__((constructor))。(感兴趣的读者可以自行查找)
通过查找,我们可以发现只有少数几个文件中定义了具有constructor属性的函数,其中就包括include/qemu/module.h文件中do_qemu_init_ ## function(void),它是一个宏,而不是一个具体的函数。它的定义如下:(代码在include/qemu/module.h中)

#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_module_init(function, type);                                   \
}
#endif

我们进一步看一下register_module_init的实现:(代码在util/module.c中)。这段代码实际上是将类型为type的代码模块中一个初始化函数的指针存入全局变量init_type_list对应类型的链表中。

void register_module_init(void (*fn)(void), module_init_type type)
{
    ModuleEntry *e;
    ModuleTypeList *l;

    e = g_malloc0(sizeof(*e));
    e->init = fn;
    e->type = type;

    l = find_type(type);  //找到全局变量init_type_list类型为type的ModuleTypeList链表

    QTAILQ_INSERT_TAIL(l, e, node);
}

这将意味着,如果在全局的代码中调用module_init(f, t),系统就会定义一个名为module_init_f的函数,并且该函数是constructor,必须在main函数之前执行,该函数要执行的代码就是下列代码。这个函数最终将函数指针f存入全局变量init_type_list数组中的type为t的链表中。

register_module_init(f, t);

而根据这个module_init宏,qemu分别针对四个代码模块定义了四种宏:

typedef enum {
    MODULE_INIT_BLOCK,
    MODULE_INIT_MACHINE,
    MODULE_INIT_QAPI,
    MODULE_INIT_QOM,
    MODULE_INIT_MAX
} module_init_type;

#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
#define type_init(function) module_init(function, MODULE_INIT_QOM)

也就是说,在全局的代码中如果调用上述任何一个函数,就会在系统中自动生成一个具有constructor属性的函数,这个函数会在main函数之前执行。举一个例子,我们在qom/object.c中看到了type_init()函数的一个调用:

static void register_types(void)
{
    static TypeInfo interface_info = {
        .name = TYPE_INTERFACE,
        .class_size = sizeof(InterfaceClass),
        .abstract = true,
    };

    static TypeInfo object_info = {
        .name = TYPE_OBJECT,
        .instance_size = sizeof(Object),
        .instance_init = object_instance_init,
        .abstract = true,
    };

    type_interface = type_register_internal(&interface_info);
    type_register_internal(&object_info);
}

type_init(register_types)

这样就会在系统中生成一个如下函数:

static void __attribute__((constructor)) do_qemu_init_register_type(void)    
{                                                                           
    register_module_init(register_type, MODULE_INIT_QOM);                                   
}

qemu中如何利用module实现大量代码的初始化工作

通过上述定义constructor属性的函数的方式,qemu会在main函数之前,将所有代码模块中的所有的初始化函数指针保存到init_type_list数组中的对应类型的链表中。我们只需调用每种类型链表中每个entry保存的初始化函数指针,就可以实现调用所有代码模块中大量的初始化函数指针。

void module_call_init(module_init_type type)
{
    ModuleTypeList *l;
    ModuleEntry *e;

    module_load(type);
    l = find_type(type);

    QTAILQ_FOREACH(e, l, node) {
        e->init();
    }
}

总结

qemu中通过设计module的机制,将大量的初始化函数分模块地保存到链表中,简化了这些代码模块的初始化工作,也简化了实现的流程,提高了代码的可维护性。
--------------------- 
原文:https://blog.csdn.net/u011364612/article/details/53581501  
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/u014022631/article/details/83585158