设备类型注册
本小节主要讲清楚type_table这个hash table的由来。
为了比较清楚的解释这个流程,在本节中以e1000这种设备为例。其代码主要集中在hw/net/e1000.c这个文件中。
TypeInfo定义设备
qemu中注册的每个设备都由一个TypeInfo类型来定义。这个定义的内容不太多,我就直接上代码了。
struct TypeInfo
{
const char *name;
const char *parent;
size_t instance_size;
void (*instance_init)(Object *obj);
void (*instance_post_init)(Object *obj);
void (*instance_finalize)(Object *obj);
bool abstract;
size_t class_size;
void (*class_init)(ObjectClass *klass, void *data);
void (*class_base_init)(ObjectClass *klass, void *data);
void (*class_finalize)(ObjectClass *klass, void *data);
void *class_data;
InterfaceInfo *interfaces;
};那对于一个e1000设备,这个类型是什么样子的呢?
暂时我们不关系其他内容,只看到定义时赋值的头两个值是:
name: 本设备类型的名称
parent:父设备类型的名称
这里可以看出在qemu设备模型中使用名称作为类型的唯一标识的,并且还存在了父子关系(这点我们在后面再讲)。
类型注册函数type_register()
定义了设备类型后,需要做的是注册这个类型。这样qemu才知道现在可以支持这种设备了。
注册的函数就是type_register(),做的工作也很简单:
通过type_new()生成一个TypeInfo对应的TypeImpl类型
并以name为关键字添加到名为type_table的一个hash table中
假如我们用一个图来描述,大概可以画成这样。
可以看到,几乎所有的成员两者是一致的。这里所做的工作就是把内容复制了一遍。
为啥不直接用呢?不懂。
何时注册设备类型
不过这些都不难,着重我想说的是type_register调用的时机。而这个过程又分了两步走:
“注册”设备注册函数
“执行”设备类型注册
这个东西是有点绕,那就以e1000为例来看。
注册设备注册函数
在e1000的实现中,我们可以看到如下的过程:
神奇的地方就在这个typeinit(),使用了一个我以前不知道的gcc方法_attribute((constructor))。
来看看函数的定义先:
因为使用了attribute((constructor))修饰,所以这个函数将会在main函数执行前被执行。那这个register_module_init()函数又干了啥呢?
所以说qemu有时候有点蛋疼。又整了一个链表,把设备类型注册函数添加到里面。在这个e1000的例子中就是e1000_register_types这个函数。
细心的朋友可能还注意到了,这个链表还分了类型。对type_init()而言,这个类型是MODULE_INIT_QOM。记住这个,后面我们将会用到。
来看一下这个注册函数的链表: init_type_list[MODULE_INIT_MAX]
这样就形成了一个注册函数的数组,不同的类型各自添加到这个数组中。
执行设备类型注册
刚才忙活了一堆,其实这个类型注册函数e1000_register_types还没有被执行到。那究竟什么时候执行真正的类型注册呢?
让偶来揭示谜团:
看到上一节中的MODULE_INIT_QOM了没有?其作用就是找到MODULE_INIT_QOM对应的链表,执行其中的init函数。也就是我们刚通过register_module_init添加进去的e1000_register_types了。
到这,终于算是把type_table这个hash table的由来说清楚了。
Last updated
Was this helpful?