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

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

本文内容参考:

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

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

特此致谢!

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

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

上一回讲解完了Java反射机制流程的第2步,再来回顾一下:

本回往下进行解析。

(3)QEMU中,TypeImpl还有一个instance_init函数,其相当于构造函数,用于根据XXXClass生成Object。这就相当于Java反射中最终创建的对象。

这一步对应的就是,QEMU中各个层次的类的instance_init会根据XXXClass生成对象,这就是实际要创建的对象。

仍以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)

virtio_blk_info中的instance_init指向的函数为virtio_blk_instance_init。该函数也在hw/block/virtio-blk.c中,代码如下:

static void virtio_blk_instance_init(Object *obj)
{
    VirtIOBlock *s = VIRTIO_BLK(obj);

    device_add_bootindex_property(obj, &s->conf.conf.bootindex,
                                  "bootindex", "/disk@0,0",
                                  DEVICE(obj));
}

这样就完成了类Java反射机制的整个流程。

再来整体回顾一下全流程:

在QEMU中,每一个Module会模拟某种设备,那么就定义一种类型TypeImpl来表示此种设备。

仍以virtio block device为例:

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)

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

这里的调用链为:

type_init()

---> type_register_static()

---> type_register()

---> type_register_internal()

---> type_new()

---> type_table_add()

重点关注type_register_internal函数。其在qom/object.c中,代码如下:

static TypeImpl *type_register_internal(const TypeInfo *info)
{
    TypeImpl *ti;
    ti = type_new(info);

    type_table_add(ti);
    return ti;
}

在type_register_internal函数中,会根据virtio_blk_info这个TypeInfo,创建一个TypeImpl,来表示这个新注册的类。换句话说,TypeImpl才是我们真正想要声明的那个类。

在QEMU中,有一个全局的哈希表type_table,用来存放所有定义的类。在type_register_internal函数中,先调用type_new函数,从全局表里头根据名称(本例中是TypeInfo virtio_blk_info)查找这个类。如果找到,则说明该类曾经被注册过,就报错;如果没有找到,则说明此类是一个新的类,那么就将TypeInfo中的信息填到TypeImpl里面。

到这一步,类初始化class_init(本例中是virtio_blk_class_init)函数还没有被调用,这个类还处于纸面的状态。

流程中的更多步骤,请看下回。