IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Qemu中的设备注册

    Liu Yutao发表于 2015-01-10 15:38:00
    love 0

    这两天在看KVM网络虚拟化相关的内容,找了很多相关的资料,也吭哧吭哧地读了那些简直无法直视的代码,终于感觉渐渐理出点头绪了。之后如果有时间的话会慢慢将这些知识整理出来,当然也包括推荐一些比较靠谱的博客和文档吧。

    今天要写的内容是关于“Qemu设备注册”,和KVM网络虚拟化没有太大的关系。其实这篇博文很简单,主要就是解决了一个我之前一直没有搞懂的最基本的问题:

    Qemu里面的设备注册函数是如何被调用的?

    废话不说,直接进入正篇。

    其实在很多设备相关的文件中都会看到代码的最后部分是这样的(比如在virtio的代码中):

    hw/virtio/virtio.c
    1
    
    type_init(virtio_register_types)
    

    问题是这个type_init是个什么东西?virtio_register_types是如何被调用的?

    如果把type_init展开的话:

    include/qemu/module.h
    1
    2
    3
    4
    5
    6
    7
    
    #define module_init(function, type)                                         \
    static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
    {                                                                           \
        register_module_init(function, type);                                   \
    }
    
    #define type_init(function) module_init(function, MODULE_INIT_QOM)
    

    可以看到这个函数前面加了一个gcc的attribute:__attribute__((constructor)),这个表示说该函数会在整个程序的main函数执行之前被执行,于是我们继续追踪这个会在main函数执行之前执行的register_module_init:

    util/module.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    static ModuleTypeList init_type_list[MODULE_INIT_MAX];
    ...
    static ModuleTypeList *find_type(module_init_type type)
    {
        ModuleTypeList *l;
    
        init_lists();
    
        l = &init_type_list[type];
    
        return l;
    }
    ...
    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);
    }
    

    可以看到,它把传进来的fn函数指针传递给了一个ModuleEntry变量的init参数,并将其插入一个叫做init_type_list的列表里面。

    另外在module_init的时候我们还传了一个参数叫做MODULE_INIT_QOM,这个参数也和init_type_list似乎有着千丝万缕的关系,那么问题来了:

    这个MODULE_INIT_QOM又是个什么东西?

    好了,实在忍不住就讲了一堆废话,从现在开始不再卖关子了,开始正常地表达人话吧,揭晓答案的一刻开始了:

    QOM,全称Qemu Object Model,它是Qemu最新的设备相关的模型,具体可以参看这里和这里。纵观Qemu的设备模型发展历史,可以用下面这张图(摘自这里)描述:

    qemu device evolution

    最开始的Qemu采用的是很混乱的ad hoc模式,每种设备都有不同的表示方式,杂乱无章,于是在2009年Paul Brook一票人实在忍不下去了,开发了QDev的设备模型,将所有的模拟设备进行了整合,变成了一种单根结点(SysBus)的树状形式,并且增加了hotplug的功能。

    但是后来由于某些原因(具体是什么我也不是很清楚,似乎是因为QDev的这种单根结点树状组织方式还是不太令人满意,特别是Device和Bus之间缠绕不清的关系),在2012年Anthony Liguori一票人又开发了一套QOM的设备模型,将原来的QDev给换掉了。

    QDev和QOM的差别主要可以参看这里,从Device Relationship,Naming和Properties三个角度来进行区分,我看下来觉得主要就是集中在Bus这么个角色里面,在QDev里面,Bus和Device好像是一种平级的关系,而在QOM里面,Bus只是Device的接口。具体的差别这里就不详说了,有兴趣的看之前的那个文档就好了。

    总之,现在Qemu采用的是QOM这种设备模型。既然如此,MODULE_INIT_QOM就是用于初始化这些相关的设备咯。

    我们可以看到,在Qemu的主函数中,一开始就有这么一句话:

    vl.c
    1
    
    module_call_init(MODULE_INIT_QOM);
    
    util/module.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    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();
        }
    }
    

    回顾下之前的type_init,也就是说,在main函数开始之前,这些设备会新建一个ModuleEntry,插入init_type_list的MODULE_INIT_QOM的项中:

    qom init

    之后在module_call_init中遍历链表中所有的entry,调用其对应的init函数,比如在我们的例子中,调用virtio_register_types函数:

    hw/virtio/virtio.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    static const TypeInfo virtio_device_info = {
        .name = TYPE_VIRTIO_DEVICE,
        .parent = TYPE_DEVICE,
        .instance_size = sizeof(VirtIODevice),
        .class_init = virtio_device_class_init,
        .abstract = true,
        .class_size = sizeof(VirtioDeviceClass),
    };
    
    static void virtio_register_types(void)
    {
        type_register_static(&virtio_device_info);
    }
    

    将virtio_device_info注册到系统中。

    以上就是Qemu中的设备如何通过type_init和main函数中的module_call_init进行注册的过程。这里再向大家推荐一个文档:

    Qemu设备模拟

    这篇文章讲的还蛮清楚的。

    至于virtio_device_info中的.class_init等函数何时被调用,则是之后的内容了,等我有心情的时候再写吧。



沪ICP备19023445号-2号
友情链接