lvyilong316 阅读(34) 评论(0)

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_QOMMODULE_INIT_OPTS这些都是枚举,定义如下:

点击(此处)折叠或打开

  1. typedef enum {
  2.     MODULE_INIT_BLOCK,
  3.     MODULE_INIT_MACHINE,
  4.     MODULE_INIT_QAPI,
  5.     MODULE_INIT_QOM,
  6.     MODULE_INIT_MAX
  7. } module_init_type;

我们先不去关注没个宏具体代表什么,我们只分析一下这个module_call_init的调用路径。这里以module_call_init(MODULE_INIT_QOM)为例子,所谓QOMQEMU Object Module,就是qemu对象模型。这是qemu中一个很重要的概念,我们以后分析。下面看module_call_init实现。

点击(此处)折叠或打开

  1. void module_call_init(module_init_type type)
  2. {
  3.     ModuleTypeList *l;
  4.     ModuleEntry *e;
  5.  
  6.     l = find_type(type);
  7.  
  8.     QTAILQ_FOREACH(e, l, node) {
  9.         e->init();
  10.     }
  11. }

我们看到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

点击(此处)折叠或打开

  1. void register_module_init(void (*fn)(void), module_init_type type)
  2. {
  3.     ModuleEntry *e;
  4.     ModuleTypeList *l;
  5.  
  6.     e = g_malloc0(sizeof(*e));
  7.     e->init = fn;
  8.     e->type = type;
  9.  
  10.     l = find_type(type);
  11.  
  12.     QTAILQ_INSERT_TAIL(l, e, node);
  13. }

    下面看register_module_init又是在哪里调用的。

点击(此处)折叠或打开

  1. #define module_init(function, type) \
  2. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
  3. { \
  4.     register_module_init(function, type); \
  5. }

我们看到有一个功能等价的宏module_init,注意其前面的修饰__attribute__((constructor))代表其在main函数之前被执行。然后我们看module_init的调用位置。在moudle.h中我们看到以下代码:

点击(此处)折叠或打开

  1. #define block_init(function) module_init(function, MODULE_INIT_BLOCK)
  2. #define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
  3. #define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
  4. #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

点击(此处)折叠或打开

  1. static void virtio_pci_register_types(void)
  2. {
  3.     type_register_static(&virtio_rng_pci_info);
  4.     type_register_static(&virtio_input_pci_info);
  5.     type_register_static(&virtio_input_hid_pci_info);
  6.     type_register_static(&virtio_keyboard_pci_info);
  7.     type_register_static(&virtio_mouse_pci_info);
  8.     type_register_static(&virtio_tablet_pci_info);
  9.     type_register_static(&virtio_host_pci_info);
  10.     type_register_static(&virtio_pci_bus_info);
  11.     type_register_static(&virtio_pci_info);
  12. #ifdef CONFIG_VIRTFS
  13.     type_register_static(&virtio_9p_pci_info);
  14. #endif
  15.     type_register_static(&virtio_blk_pci_info);
  16.     type_register_static(&virtio_scsi_pci_info);
  17.     type_register_static(&virtio_balloon_pci_info);
  18.     type_register_static(&virtio_serial_pci_info);
  19.     type_register_static(&virtio_net_pci_info);
  20. #ifdef CONFIG_VHOST_SCSI
  21.     type_register_static(&vhost_scsi_pci_info);
  22. #endif
  23. }

     又是一系列的type_register_static调用,type_register_static又经过如下调用最终调用了type_register_internal

type_register_staticà type_registerà type_register_internal

点击(此处)折叠或打开

  1. static TypeImpl *type_register_internal(const TypeInfo *info)
  2. {
  3.     TypeImpl *ti;
  4.     ti = type_new(info);
  5.  
  6.     type_table_add(ti);
  7.     return ti;
  8. }

就是根据传入的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有这样一个宏:

点击(此处)折叠或打开

  1. #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \
  2.     static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \
  3.     { \
  4.         MachineClass *mc = MACHINE_CLASS(oc); \
  5.         optsfn(mc); \
  6.         mc->name = namestr; \
  7.         mc->init = initfn; \
  8.     } \
  9.     static const TypeInfo pc_machine_type_##suffix = { \
  10.         .name = namestr TYPE_MACHINE_SUFFIX, \
  11.         .parent = TYPE_PC_MACHINE, \
  12.         .class_init = pc_machine_##suffix##_class_init, \
  13.     }; \
  14.     static void pc_machine_init_##suffix(void) \
  15.     { \
  16.         type_register(&pc_machine_type_##suffix); \
  17.     } \
  18. machine_init(pc_machine_init_##suffix)

也就是说pc_machine_init_xxx这类函数会通过DEFINE_PC_MACHINE这个宏注册。

2.       我们看下DEFINE_PC_MACHINE这个宏的相关调用,在hw\i386\pc_piix.c中发现如下宏定义:

点击(此处)折叠或打开

  1. #define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
  2.     static void pc_init_##suffix(MachineState *machine) \
  3.     { \
  4.         void (*compat)(MachineState *m) = (compatfn); \
  5.         if (compat) { \
  6.             compat(machine); \
  7.         } \
  8.         pc_init1(machine); \
  9.     } \
  10. DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)

也就是说pc_machine_init_xxx这类函数可以由DEFINE_I440FX_MACHINE来定义。怎么定义不是重点,重点是可以看到pc_machine_init_xxx中一定会调用pc_init1,而pc_init1对之后的代码分析是一个关键函数。