QEMU ソース コードの完全な分析 9 - QEMU モジュールの定義 (1)

前回記事の続き:QEMUソースコードの完全解析その8 —— 全モジュールを初期化する

この記事の内容に関する参考文献:

「 Linux オペレーティング システムに関する興味深い話」 —— Liu Chao、Geek Time

QEMU /KVM」ソースコード分析とアプリケーション - Li Qiang、機械産業プレス

どうもありがとうございます!

QEMU モジュールを定義すると、type_init が呼び出されます。kvm を例に挙げると、kvm モジュールは accel/kvm/kvm-all.c に実装されています。このファイルには、次の 1 行のコードがあります。

type_init(kvm_type_init);

type_init はマクロ定義であり、include/qemu/module.h 内のコードは次のとおりです。

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

したがって、type_init(kvm_type_init) は次のように展開されます。

module_init(kvm_type_init, MODULE_INIT_QOM)

module_init の定義は include/qemu/module.h にあり、コードは次のとおりです。

#ifdef BUILD_DSO
void DSO_STAMP_FUN(void);
/* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can
 * distinguish "version mismatch" from "not a QEMU module", when the stamp
 * check fails during module loading */
void qemu_module_dummy(void);

#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_dso_module_init(function, type);                               \
}
#else
/* This should not be used directly.  Use block_init etc. instead.  */
#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_module_init(function, type);                                   \
}
#endif

BUILD_DSO はシステムで定義されていないため、実際には #else 分岐、つまり次のコードを受け取ります。

/* This should not be used directly.  Use block_init etc. instead.  */
#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_module_init(function, type);                                   \
}

module_init(kvm_type_init, MODULE_INIT_QOM) を次のように置き換えます。

static void __attribute__((constructor)) do_qemu_init_kvm_type_init(void)
{
    register_module_init(kvm_type_init, MODULE_INIT_QOM);
}

register_module_init は util/module.c にあり、コードは次のとおりです。

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

前回分析した find_type 関数も参照してください。ここで、理解を助けるために、同じファイル (実際には register_module_init の上) にコードを再度貼り付けます。

static ModuleTypeList *find_type(module_init_type type)
{
    init_lists();

    return &init_type_list[type];
}

QTAIL_INSERT_TAIL は当然 include/qemu/queue.h で定義されており、コードは次のとおりです。

#define QTAILQ_INSERT_TAIL(head, elm, field) do {                       \
        (elm)->field.tqe_next = NULL;                                   \
        (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev;     \
        (head)->tqh_circ.tql_prev->tql_next = (elm);                    \
        (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ;             \
} while (/*CONSTCOND*/0)

したがって、 register_module_init(kvm_type_init, MODULE_INIT_QOM) の場合、 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->init = kvm_type_init;
    e->type = type; //实际上是:e->type = MODULE_INIT_QOM;

    l = find_type(type); //实际上是:l = find_type(MODULE_INIT_QOM);

    QTAILQ_INSERT_TAIL(l, e, node);
}

type_init(kvm_type_init) の場合、最後に MODULE_INIT_QOM のタイプである register_module_init(kvm_type_init, MODULE_INIT_QOM) を呼び出します。MODULE_INIT_QOM のタイプに属するモジュールリスト ModuleTypeList (前回解析) があり、その中に ModuleEntry 項目が 1 つずつ存在します。KVM もその 1 つで、もちろん他にもマシン、CPU など多くの項目があります。各項目を初期化する init 関数は、パラメータで表される関数 fn です。KVM モジュールの場合、その init 関数は kvm_type_init、Machine モジュールの場合、その init 関数は machine_register_types です。最後に、前回解析した module_call_init 関数では、ここで割り当てた初期化 fn を実際に呼び出して、実際の初期化作業を行っています。

次回はKVMの初期化関数kvm_type_initを見ていきます。

おすすめ

転載: blog.csdn.net/phmatthaus/article/details/131923111