QEMU 源码目录简介

最近在研究QEMU,读了一些QEMU的源码,因为涉及的东西比较多,找到的资料又都比较破碎,不太完整。所以将最近的成果总结一下。
相比其他的开源软件来说,QEMU源码下面目录比较多,下面就先把这些目录的内容大致整理一下。
docs/ 包含了一些文档,说实话,对初学者来说,读这些文档压根没有头绪
hw/   包含了所有支持的硬件设备
include/  包含了一些头文件
linux-user/  包含了linux下的用户模式的代码
target-XXX/   包含了QEMU目前所支持guset端的处理器架构。包 括:alpha,arm,cris,i386,lm32,m68k,microblaze,mips,openrisc,ppc,s390x,sh4,sparc,unicore32,xtensa. 此处的XXX就是指这其中的一种架构。包含的代码的主要功能是将该guest架构的指令翻译成TCG OP代码。也就是target-arm下的代码就是将arm架构的指令翻译成TCG OP。这些目录占了源码目录的很大一部分。
tcg/   包含了动态翻译工具tcg的源码部分,主要是将TCG OP转化为host binary的部分。这个目录下也包含了多个架构名字命名的目录,每个目录下存放着针对该架构的代码。后续会详细介绍。
test/ 从名字上可以看出,应该是存放测试部分的代码,但是目前这部分代码还没读。
TCG

TCG 是Tiny Code Generator的简称,它之前是一个后端编译器,现在是作为一个动态翻译器来使用。在QEMU中,它主要用来将虚拟出来的系统的指令转化成真正硬件支 持的指令中的从中间代码到硬件支持的机器代码的过程。前端的将指令翻译成中间代码的过程,是一个反汇编的过程。

反汇编的过程的源码的主要地址:qemu source code/target-XXX。此处的XXX指的是模拟出来的系统的架构。

TCG的源码的位置是:qemu source code/tcg。这个目录下有很多文件夹,每个文件夹都代表一个目标架构。这里的目标架构指的是真正的硬件架构,也就是说运行QEMU的架构。

在qemu source code/tcg目录下,有一个README文件,介绍了tcg的主要内容。

在qemu source code/tcg/arm目录下,只有两个文件,实现了生成arm架构的内容。tcg-target.c和tcg-target.h两个文件。和arm同 级目录的ia64,hppa,ppc,s390,i386,mips,ppc64,sparc,tci等目录下也是同样的名字的两个文件,当然文件的内容 并不相同。关于这两个文件的内容,README是这么描述的:tcg-target.h contains the target specific definitions. tcg-target.c contains the target specific code.

动态翻译只是在必要的时候才进行翻译,而尽可能的将时间花费在执行host code上。TB(Translation Block)翻译之后得到的host code会存放在code cache中,因为有很多TB会被重复执行,所以这样会达到更加的效果。

user model之linux

从源码目录来看,user model有两块内容bsd-user和linux-user。我主要研究了下linux-user这种情况。

首先要提一下通常容易关注的焦点,linux-user下的函数入口点:/源码目录/linux-user/main.c中的
Line:3388    int main(int argc, char **argv, char **envp).
找到了入口函数,就可以根据这个main函数中的调用关系来看看这个情况下的主要执行流程和动作了。

int main(int argc, char **argv, char **envp)
{

    module_call_init(MODULE_INIT_QOM);

    qemu_cache_utils_init(envp);

    /*初始化了tcg的相关部分,包含了cpu动态转化的一些初始化操作。*/
    tcg_exec_init(0);
    cpu_exec_init_all();

    /*包含了虚拟cpu的初始化*/
    env = cpu_init(cpu_model);
 
    /*加载可执行程序,即Guest code*/
    ret = loader_exec(filename, target_argv, target_environ, regs,
        info, &bprm);


    target_set_brk(info->brk);
    /*系统调用初始化*/
    syscall_init();
    /*信号初始化*/
    signal_init();

   /*此函数是主要的循环体,通过这个函数来实现对指令的动态翻译,并且执行翻译之后的Host Code。
   通过最终调用cpu_gen_code()函数(位于translate-all.c文件中)来实现
    动态翻译,其中调用了两个关键函数。一个关键函数是gen_intermediate_code()
   函数(位于target-arm/translate.c,此处以guest指令集为arm为例,其他的可以自行替换),
   这个函数的主要功能是根据Guest Code生成TCG Operations。另外一个重要的函数是
  tcg_gen_code()函数(位于tcg/tcg.c),这个函数主要是把TCG Operations转化成Host code。*/
    cpu_loop(env);
    /* never exits */
 
    return 0;
}

下面来分析下刚才介绍的重要函数cpu_loop(). cpu_loop()函数在linux-user/main.c中有多个版本,区别在于参数,参数是不同的cpu state,下面举例仍然以arm为主。

void cpu_loop(CPUARMState *env)
{
    int trapnr;
    unsigned int n, insn;
    target_siginfo_t info;
    uint32_t addr;

    for(;;) {
        cpu_exec_start(env);
        trapnr = cpu_arm_exec(env);
        cpu_exec_end(env);
   ...............
}
可以看到for循环里有三个函数调用,分别是cpu_exec_start,cpu_arm_exec,cpu_exec_end。其中最重要的
cpu_arm_exec函数,通过target-arm/cpu.h中的宏定义#define cpu_exec cpu_arm_exec调用了cpu-exec.c文件
中的cpu_exec()函数。
cpu_exec()是整个qemu中的一个重要函数,它负责整个核心的从guest code 到host code的翻译和执行。
cpu_exec()首先会去调用tb_find_fast(),tb_find_fast()会判断取回来的tb是否合法,如果不合法会去调用tb_find_slow()函数。
tb_find_slow()会试图通过物理mapping去寻找tb,如果寻找失败则会调用tb_gen_code()去翻译代码。
cpu_exec()函数调用tb_find_fast()之后会调用tcg_qemu_tb_exec()去执行所找到的tb。最后再调用cpu_exec_nocache()去执行剩下的代码。
### QEMU 源码详细分析与内部实现 QEMU 是一个开源的虚拟化平台,其源码结构复杂且功能强大。以下是对 QEMU 内部实现和代码结构的详细分析。 #### 1. 源码目录结构 QEMU源码组织方式清晰,主要分为以下几个部分[^2]: - **`qemu/`**:主目录,包含项目的顶层文件。 - **`qemu/target/`**:每个目标架构的仿真代码,例如 x86、ARM 等。 - **`qemu/cpu-exec.c`**:CPU 执行的核心逻辑,负责调度和执行动态生成的代码。 - **`qemu/tcg/`**:翻译块(TB)生成和优化的相关代码,用于将客户机指令转换为主机指令。 - **`qemu/exec.c`**:内存管理的核心实现,包括物理地址到虚拟地址的映射。 - **`qemu/hw/`**:硬件设备的模拟代码,支持各种虚拟硬件设备。 #### 2. 动静代码分离与入口点 在 QEMU 的执行流程中,静态代码(static code)和动态代码(dynamic code)之间的交互是核心之一。静态代码负责初始化、控制流管理和资源分配,而动态代码则是通过 TCG(Tiny Code Generator)生成的主机代码[^1]。 动态代码的入口点由 `code_gen_prologue` 提供,这是一种类似函数序言(prologue)的方式,确保动态生成的代码能够被正确调用和执行[^1]。 #### 3. TCG 工作机制 TCG 是 QEMU 的关键组件,负责将客户机指令翻译成主机指令。其工作流程如下: - **指令解码**:解析客户机指令并将其分解为基本操作。 - **中间表示(IR)生成**:将解码后的指令转换为统一的中间表示。 - **代码生成**:将 IR 转换为主机平台的机器码,并存储在翻译块(TB)中。 - **缓存管理**:通过 LRU 缓存策略管理 TB,提高执行效率。 以下是 TCG 相关的核心代码片段: ```c // qemu/tcg/tcg.c void tcg_gen_code(TCGContext *s) { // 根据中间表示生成主机代码 gen_prologue(s); // 生成 prologue 代码 for (int i = 0; i < s->gen_op_idx; i++) { gen_op(s, s->gen_opparam_ptr[i]); } } ``` #### 4. 内存管理 QEMU 的内存管理系统实现了客户机物理地址到主机虚拟地址的映射。核心文件 `exec.c` 包含了内存访问的实现细节[^3]。 以下是内存访问的核心逻辑: ```c // qemu/exec.c target_ulong cpu_ldl_code(CPUState *cs, target_ulong addr) { // 加载客户机地址处的 32 位数据 return ldq_p(addr); } ``` #### 5. 设备模拟 QEMU 支持多种硬件设备的模拟,这些设备的代码位于 `hw/` 目录下。每个设备都有独立的模块化实现,便于扩展和维护。 以下是一个简单设备的注册示例: ```c // qemu/hw/misc/example_device.c static void example_device_realize(DeviceState *dev, Error **errp) { // 初始化设备 } static void example_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = example_device_realize; } static const TypeInfo example_device_info = { .name = TYPE_EXAMPLE_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(ExampleDevice), .class_init = example_device_class_init, }; static void example_device_register_types(void) { type_register_static(&example_device_info); } type_init(example_device_register_types) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值