> For the complete documentation index, see [llms.txt](https://richardweiyang-2.gitbook.io/understanding_qemu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://richardweiyang-2.gitbook.io/understanding_qemu/00-devices/01-type_register.md).

# 设备类型注册

本小节主要讲清楚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设备，这个类型是什么样子的呢？

```
#define TYPE_E1000_BASE "e1000-base"

static const TypeInfo e1000_base_info = {
    .name          = TYPE_E1000_BASE,
    .parent        = TYPE_PCI_DEVICE,
    .instance_size = sizeof(E1000State),
    .instance_init = e1000_instance_init,
    .class_size    = sizeof(E1000BaseClass),
    .abstract      = true,
    .interfaces = (InterfaceInfo[]) {
        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
        { },
    },
};
```

暂时我们不关系其他内容，只看到定义时赋值的头两个值是：

* name：  本设备类型的名称
* parent：父设备类型的名称

这里可以看出在qemu设备模型中使用**名称作为类型的唯一标识**的，并且还存在了父子关系（这点我们在后面再讲）。

## 类型注册函数type\_register()

定义了设备类型后，需要做的是注册这个类型。这样qemu才知道现在可以支持这种设备了。

注册的函数就是type\_register()，做的工作也很简单：

* 通过type\_new()生成一个TypeInfo对应的TypeImpl类型
* 并以name为关键字添加到名为type\_table的一个hash table中

假如我们用一个图来描述，大概可以画成这样。

```
        type_table(GHashTable)  ; this is a hash table with name as the key
       +-----------------------+
       |                       |
       +-----------------------+
                |
                v
       +--------------------------+                   +----------------------------+
       |TypeImpl*                 | <--- type_new()   | TypeInfo                   |
       |    name                  |                   |     name                   |
       |    parent                |                   |     parent                 |
       |                          |                   |                            |
       |    class_size            |                   |     class_size             |
       |    class_init            |                   |     class_init             |
       |    class_base_init       |                   |     class_base_init        |
       |    class_finalize        |                   |     class_finalize         |
       |    class_data            |                   |     class_data             |
       |                          |                   |                            |
       |    instance_size         |                   |     instance_size          |
       |    instance_init         |                   |     instance_init          |
       |    instance_post_init    |                   |     instance_post_init     |
       |    instance_finalize     |                   |     instance_finalize      |
       |                          |                   |                            |
       |    abstract              |                   |     abstract               |
       |    interfaces            |                   |     interfaces             |
       |    num_interfaces        |                   |     num_interfaces         |
       +--------------------------+                   +----------------------------+
                |
                v
       +--------------------------+                   +----------------------------+
       |TypeImpl*                 | <--- type_new()   | TypeInfo                   |
       |    TYPE_MACHINE          |                   |     TYPE_MACHINE           |
       +--------------------------+                   +----------------------------+
```

可以看到，几乎所有的成员两者是一致的。这里所做的工作就是把内容复制了一遍。

为啥不直接用呢？不懂。

## 何时注册设备类型

不过这些都不难，着重我想说的是type\_register调用的时机。而这个过程又分了两步走：

* “**注册**”设备注册函数
* “**执行**”设备类型注册

这个东西是有点绕，那就以e1000为例来看。

### 注册设备注册函数

在e1000的实现中，我们可以看到如下的过程：

```
static void e1000_register_types(void)
{
    ...
    type_register(&type_info);
}

type_init(e1000_register_types)
```

神奇的地方就在这个type*init()，使用了一个我以前不知道的gcc方法\_attribute*((constructor))。

来看看函数的定义先：

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

#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_module_init(function, type);                                   \
}
```

因为使用了**attribute**((constructor))修饰，所以这个函数将会在main函数执行前被执行。那这个register\_module\_init()函数又干了啥呢？

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

所以说qemu有时候有点蛋疼。又整了一个链表，把设备类型注册函数添加到里面。在这个e1000的例子中就是e1000\_register\_types这个函数。

细心的朋友可能还注意到了，这个链表还分了类型。对type\_init()而言，这个类型是**MODULE\_INIT\_QOM**。记住这个，后面我们将会用到。

来看一下这个注册函数的链表: init\_type\_list\[MODULE\_INIT\_MAX]

```
    init_type_list[MODULE_INIT_MAX]
    +-----------------------------+
    |MODULE_INIT_BLOCK            |
    |                             |
    |                             |
    +-----------------------------+
    |MODULE_INIT_OPTS             |
    |                             |
    |                             |
    +-----------------------------+      +-----------------------+     +-----------------------+
    |MODULE_INIT_QOM              | ---->|                       |---->|                       |
    |                             |      |e1000_register_types   |     |pc_dimm_register_types |
    |                             |      |   --> type_register() |     |   --> type_register() |
    +-----------------------------+      +-----------------------+     +-----------------------+
    |MODULE_INIT_TRACE            |
    |                             |
    |                             |
    +-----------------------------+
```

这样就形成了一个注册函数的数组，不同的类型各自添加到这个数组中。

### 执行设备类型注册

刚才忙活了一堆，其实这个类型注册函数e1000\_register\_types还没有被执行到。那究竟什么时候执行真正的类型注册呢？

让偶来揭示谜团：

```
  main()
    module_call_init(MODULE_INIT_QOM);
    {
      ModuleTypeList *l;
      ModuleEntry *e;

      l = find_type(type);

      QTAILQ_FOREACH(e, l, node) {
      e->init();
      }
    }
```

看到上一节中的MODULE\_INIT\_QOM了没有？其作用就是找到MODULE\_INIT\_QOM对应的链表，执行其中的init函数。也就是我们刚通过register\_module\_init添加进去的e1000\_register\_types了。

到这，终于算是把type\_table这个hash table的由来说清楚了。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://richardweiyang-2.gitbook.io/understanding_qemu/00-devices/01-type_register.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
