QEMU源码全解析 —— 块设备虚拟化(6)

接前一篇文章: QEMU源码全解析 —— 块设备虚拟化(5)

本文内容参考:

《趣谈Linux操作系统》 —— 刘超,极客时间

《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社

特此致谢!

QEMU初始化阶段的块设备虚拟化

从模板生成类和类的实例化

上一回在讲解QEMU中类继承机制的时候,讲到了Java反射机制。本回结合Java反射机制,具体看一下QEMU中从模板生成类和类实例化的相关代码。

再来回顾一下Java反射(Reflection)机制的流程。

(1)在Java中,对于一个类,首先在编写代码的时候要写一个class xxx的定义,编译好就放在.class文件中,这是处于纸面的状态。

这一步对应的就是QEMU中各个类的type_init函数,以及其调用的type_register_static函数。

以virtio block device这一层为例(hw/block/virtio-blk.c中):

static const TypeInfo virtio_blk_info = {
    .name = TYPE_VIRTIO_BLK,
    .parent = TYPE_VIRTIO_DEVICE,
    .instance_size = sizeof(VirtIOBlock),
    .instance_init = virtio_blk_instance_init,
    .class_init = virtio_blk_class_init,
};
 
static void virtio_register_types(void)
{
    type_register_static(&virtio_blk_info);
}
 
type_init(virtio_register_types)

定义一个QEMU模块会调用type_init,其是一个宏,在include/qemu/module.h中定义,如下:

#define type_init(function) module_init(function, MODULE_INIT_QOM)

由type_init宏的定义可以看出,type_init()中的参数是一个函数。此处,调用type_init(virtio_register_types)就相当于调用module_init(virtio_register_types, MODULE_INIT_QOM)。

MODULE_INIT_QOM是一个枚举值,其定义就在上边,如下:

typedef enum {
    MODULE_INIT_MIGRATION,
    MODULE_INIT_BLOCK,
    MODULE_INIT_OPTS,
    MODULE_INIT_QOM,
    MODULE_INIT_TRACE,
    MODULE_INIT_XEN_BACKEND,
    MODULE_INIT_LIBQOS,
    MODULE_INIT_FUZZ_TARGET,
    MODULE_INIT_MAX
} module_init_type;

module_init也是一个宏,其定义也在include/qemu/module.h中,如下:

#ifdef BUILD_DSO
……
#else
/* This should not be used directly.  Use block_init etc. instead.  */
#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中,代码如下:

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);
}

属于MODULE_INIT_QOM这种类型的,有一个Module列表ModuleTypeList,列表中是一项一项的ModuleEntry,并且会初始化每一项的init函数为参数fn即function,在这里为virtio_register_types。

当然,MODULE_INIT_QOM这种类型会有很多的module,所有调用type_init()的地方都注册了一个MODULE_INIT_QOM类型的Module。

每一种Module既然要模拟某种(类)设备,那么就应该定义一种类型TypeImpl来表示这种设备。这其实是一种面向对象编程思想,只不过在QEMU中用的是纯C语言的实现。所以需要变相实现类和对象。

仍以上边的代码为例:

static const TypeInfo virtio_blk_info = {
    .name = TYPE_VIRTIO_BLK,
    .parent = TYPE_VIRTIO_DEVICE,
    .instance_size = sizeof(VirtIOBlock),
    .instance_init = virtio_blk_instance_init,
    .class_init = virtio_blk_class_init,
};
 
static void virtio_register_types(void)
{
    type_register_static(&virtio_blk_info);
}
 
type_init(virtio_register_types)

virtio_register_types函数会调用type_register_static函数,注册virtio_blk_info,可以认为这样就动态定义了一个类。这个类的名字是TYPE_VIRTIO_BLK(即"virtio-blk-device");其父类为TYPE_VIRTIO_DEVICE(即"virtio-device");类初始化函数为virtio_blk_class_init;如果用这个类声明一个对象,对象的大小应该为instance_size即virtio_blk_instance_init。

这样,类比Java反射的第一步所做的工作就完成了。

更多内容请看下回。