这两天在看KVM网络虚拟化相关的内容,找了很多相关的资料,也吭哧吭哧地读了那些简直无法直视的代码,终于感觉渐渐理出点头绪了。之后如果有时间的话会慢慢将这些知识整理出来,当然也包括推荐一些比较靠谱的博客和文档吧。
今天要写的内容是关于“Qemu设备注册”,和KVM网络虚拟化没有太大的关系。其实这篇博文很简单,主要就是解决了一个我之前一直没有搞懂的最基本的问题:
Qemu里面的设备注册函数是如何被调用的?
废话不说,直接进入正篇。
其实在很多设备相关的文件中都会看到代码的最后部分是这样的(比如在virtio的代码中):
1
|
|
问题是这个type_init
是个什么东西?virtio_register_types是如何被调用的?
如果把type_init
展开的话:
1 2 3 4 5 6 7 |
|
可以看到这个函数前面加了一个gcc的attribute:__attribute__((constructor))
,这个表示说该函数会在整个程序的main
函数执行之前被执行,于是我们继续追踪这个会在main
函数执行之前执行的register_module_init
:
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 |
|
可以看到,它把传进来的fn
函数指针传递给了一个ModuleEntry
变量的init
参数,并将其插入一个叫做init_type_list
的列表里面。
另外在module_init
的时候我们还传了一个参数叫做MODULE_INIT_QOM
,这个参数也和init_type_list
似乎有着千丝万缕的关系,那么问题来了:
这个MODULE_INIT_QOM又是个什么东西?
好了,实在忍不住就讲了一堆废话,从现在开始不再卖关子了,开始正常地表达人话吧,揭晓答案的一刻开始了:
QOM
,全称Qemu Object Model
,它是Qemu最新的设备相关的模型,具体可以参看这里和这里。纵观Qemu的设备模型发展历史,可以用下面这张图(摘自这里)描述:
最开始的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的主函数中,一开始就有这么一句话:
1
|
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
回顾下之前的type_init
,也就是说,在main
函数开始之前,这些设备会新建一个ModuleEntry
,插入init_type_list
的MODULE_INIT_QOM
的项中:
之后在module_call_init
中遍历链表中所有的entry,调用其对应的init
函数,比如在我们的例子中,调用virtio_register_types
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
将virtio_device_info
注册到系统中。
以上就是Qemu中的设备如何通过type_init
和main
函数中的module_call_init
进行注册的过程。这里再向大家推荐一个文档:
这篇文章讲的还蛮清楚的。
至于virtio_device_info
中的.class_init
等函数何时被调用,则是之后的内容了,等我有心情的时候再写吧。