qemu函数调用关系—module_call_init机制
——lvyilong316
(说明:以下代码来自qemu 2.4)
在分析qemu代码前,首先要清楚代码的大概调用路径。这里对几个重要的初始化调用路径分析一下,为以后阅读代码打下基础。
首先,从main函数开始分析(vl.c),其中我们看到有几处module_call_init的调用,依次是:
module_call_init(MODULE_INIT_QOM);
module_call_init(MODULE_INIT_MACHINE);
MODULE_INIT_QOM,MODULE_INIT_OPTS这些都是枚举,定义如下:
-
typedef enum {
-
MODULE_INIT_BLOCK,
-
MODULE_INIT_MACHINE,
-
MODULE_INIT_QAPI,
-
MODULE_INIT_QOM,
-
MODULE_INIT_MAX
-
} module_init_type;
我们先不去关注没个宏具体代表什么,我们只分析一下这个module_call_init的调用路径。这里以module_call_init(MODULE_INIT_QOM)为例子,所谓QOM即QEMU Object Module,就是qemu对象模型。这是qemu中一个很重要的概念,我们以后分析。下面看module_call_init实现。
-
void module_call_init(module_init_type type)
-
{
-
ModuleTypeList *l;
-
ModuleEntry *e;
-
-
l = find_type(type);
-
-
QTAILQ_FOREACH(e, l, node) {
-
e->init();
-
}
-
}
我们看到l是一个链表,根据传入的类型返回一个链表,实际上这里用了一个全局二级链表:init_type_list,其定义如下:
static
ModuleTypeList init_type_list[MODULE_INIT_MAX];
module_call_init(MODULE_INIT_QOM)就是逐个调用init_type_list[MODULE_INIT_QOM]这个链表上所有节点(ModuleEntry)的init函数。下面的问题就是这个链表上的节点是什么时候注册的?下面开始倒着分析,首先register_module_init负责创建ModuleEntry,然后将其注册在对应的ModuleTypeList上。
l register_module_init
-
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);
-
-
QTAILQ_INSERT_TAIL(l, e, node);
-
}
下面看register_module_init又是在哪里调用的。
-
#define module_init(function, type) \
-
static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
-
{ \
-
register_module_init(function, type); \
-
}
我们看到有一个功能等价的宏module_init,注意其前面的修饰__attribute__((constructor))代表其在main函数之前被执行。然后我们看module_init的调用位置。在moudle.h中我们看到以下代码:
-
#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)
所以这四类ModuleEntry的注册根本上是由这四个宏完成的,而MODULE_INIT_QOM的注册就是由type_init完成的。要查看type_init在哪里调用,那真是太多了,整个qemu代码中不下上百处,几乎每种类型的设备都会对应一个QOM,所以都会调用type_init注册。我们仅以virtio-pci这种类型的设备为例,在virtio-pci.c中有如下调用:
type_init(virtio_pci_register_types);
那么在module_call_init(MODULE_INIT_QOM)的调用中virtio_pci_register_types就会被执行。我们再来看看virtio_pci_register_types做了什么?
l virtio_pci_register_types
-
static void virtio_pci_register_types(void)
-
{
-
type_register_static(&virtio_rng_pci_info);
-
type_register_static(&virtio_input_pci_info);
-
type_register_static(&virtio_input_hid_pci_info);
-
type_register_static(&virtio_keyboard_pci_info);
-
type_register_static(&virtio_mouse_pci_info);
-
type_register_static(&virtio_tablet_pci_info);
-
type_register_static(&virtio_host_pci_info);
-
type_register_static(&virtio_pci_bus_info);
-
type_register_static(&virtio_pci_info);
-
#ifdef CONFIG_VIRTFS
-
type_register_static(&virtio_9p_pci_info);
-
#endif
-
type_register_static(&virtio_blk_pci_info);
-
type_register_static(&virtio_scsi_pci_info);
-
type_register_static(&virtio_balloon_pci_info);
-
type_register_static(&virtio_serial_pci_info);
-
type_register_static(&virtio_net_pci_info);
-
#ifdef CONFIG_VHOST_SCSI
-
type_register_static(&vhost_scsi_pci_info);
-
#endif
-
}
又是一系列的type_register_static调用,type_register_static又经过如下调用最终调用了type_register_internal;
type_register_staticà type_registerà type_register_internal
-
static TypeImpl *type_register_internal(const TypeInfo *info)
-
{
-
TypeImpl *ti;
-
ti = type_new(info);
-
-
type_table_add(ti);
-
return ti;
-
}
就是根据传入的TypeInfo创建对应的TypeImpl,然后插入到一个hash表中。至于这些结果什么作用,什么关系不是这里的重点。
接下来我们再分析一下module_call_init(MODULE_INIT_MACHINE)的调用,因为这个和我们之后的内存管理分析有关。
1. 首先,module_call_init(MODULE_INIT_MACHINE)调用的是machine_init注册的ModuleEntry.init函数。我们只关注和pc有关的注册,其中在include\hw\i386\pc.h有这样一个宏:
-
#define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \
-
static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \
-
{ \
-
MachineClass *mc = MACHINE_CLASS(oc); \
-
optsfn(mc); \
-
mc->name = namestr; \
-
mc->init = initfn; \
-
} \
-
static const TypeInfo pc_machine_type_##suffix = { \
-
.name = namestr TYPE_MACHINE_SUFFIX, \
-
.parent = TYPE_PC_MACHINE, \
-
.class_init = pc_machine_##suffix##_class_init, \
-
}; \
-
static void pc_machine_init_##suffix(void) \
-
{ \
-
type_register(&pc_machine_type_##suffix); \
-
} \
-
machine_init(pc_machine_init_##suffix)
也就是说pc_machine_init_xxx这类函数会通过DEFINE_PC_MACHINE这个宏注册。
2. 我们看下DEFINE_PC_MACHINE这个宏的相关调用,在hw\i386\pc_piix.c中发现如下宏定义:
-
#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
-
static void pc_init_##suffix(MachineState *machine) \
-
{ \
-
void (*compat)(MachineState *m) = (compatfn); \
-
if (compat) { \
-
compat(machine); \
-
} \
-
pc_init1(machine); \
-
} \
-
DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
也就是说pc_machine_init_xxx这类函数可以由DEFINE_I440FX_MACHINE来定义。怎么定义不是重点,重点是可以看到pc_machine_init_xxx中一定会调用pc_init1,而pc_init1对之后的代码分析是一个关键函数。
阅读(6904) | 评论(0) | 转发(0) |