news 2026/9/6 5:17:30

QEMU CPU执行源码分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
QEMU CPU执行源码分析

摘要:本文从源码层面剖析 QEMU 的 CPU 执行流程,围绕主循环调度、基本块缓存、TCG 动态翻译与主机代码执行四条主线展开。文章先介绍 TCG、CPUState、CPUClass 等核心概念,再依次分析 vCPU 线程入口、cpu_exec 主循环、基本块查找与翻译、TCG 翻译流程、主机代码执行以及异常与中断处理,帮助读者建立从顶层循环到底层翻译执行的完整认知。

1. 引言

QEMU 是一款功能强大的开源模拟器,支持多种 CPU 架构的模拟与虚拟化。理解 QEMU 的 CPU 执行流程,是深入掌握其整体架构的关键。本文将从源码层面剖析 QEMU 如何加载、翻译并执行客户机 CPU 指令,帮助读者建立从顶层循环到底层翻译执行的完整认知。

2. 核心概念与架构概览

在深入源码之前,先厘清几个核心概念:

  • TCG(Tiny Code Generator):QEMU 的动态二进制翻译后端,将客户机指令翻译为主机指令。
  • CPUState:描述单个 CPU 核心状态的统一结构体,包含寄存器、异常状态等。
  • CPUClass:定义 CPU 的操作回调,如复位、执行、同步状态等。
  • AccelState:加速器抽象层,TCG 与 KVM 均通过该接口接入。

QEMU 的 CPU 执行整体流程可概括为:主循环获取 CPU 状态,调用 CPUClass 的执行回调,进入 TCG 翻译与执行循环,最终完成指令的翻译、缓存与执行。

3. 主循环与 CPU 执行入口

QEMU 的主循环位于softmmu/cpus.ccpus-common.c中。以 TCG 模式为例,每个 vCPU 线程运行qemu_tcg_cpu_thread_fn,其核心逻辑如下:

static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *cpu = arg; int r; rcu_register_thread(); qemu_mutex_lock_iothread(); qemu_thread_get_self(cpu->thread); cpu->thread_id = qemu_get_thread_id(); cpu->can_do_io = 1; current_cpu = cpu; r = qemu_init_cpu_loop(cpu); if (r) { error_report("qemu_init_cpu_loop failed: %d", r); exit(1); } qemu_mutex_unlock_iothread(); /* wait for initial kick-off */ qemu_mutex_lock_iothread(); while (!cpu->stop) { qemu_cond_wait(cpu->halt_cond, &&iothread_mutex); } qemu_mutex_unlock_iothread(); /* start the cpu */ cpu_exec(cpu); /* terminate the cpu */ qemu_mutex_lock_iothread(); qemu_cpu_is_self(cpu); current_cpu = NULL; qemu_mutex_unlock_iothread(); rcu_unregister_thread(); return NULL; }

线程启动后,主循环调用cpu_exec进入真正的执行阶段。该函数是 CPU 执行的核心入口,位于accel/tcg/cpu-exec.c

4. cpu_exec 主循环分析

cpu_exec是 TCG 模式下 CPU 执行的主循环,其简化流程如下:

int cpu_exec(CPUState *cpu) { int ret; SyncClocks sc = { 0 }; /* 进入执行前同步时钟 */ sync_clocks(&sc); /* 初始化执行上下文 */ cpu_exec_enter(cpu); /* 主执行循环 */ while (!cpu->exit_request) { TranslationBlock *tb; uintptr_t last_tb = 0; int tb_exit = 0; /* 处理中断与异常 */ if (cpu->exception_index >= 0) { /* 处理异常,可能返回用户态或重新调度 */ ret = cpu_handle_exception(cpu, &ret); if (ret != 0) { break; } } /* 处理中断 */ if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { cpu_handle_interrupt(cpu, &last_tb); } /* 查找或翻译基本块 */ tb = tb_find(cpu, last_tb, tb_exit); if (!tb) { /* 翻译失败或需要重新翻译 */ break; } /* 执行翻译后的代码 */ cpu_tb_exec(cpu, tb); /* 记录执行状态 */ last_tb = (uintptr_t)tb; tb_exit = cpu->tb_exit; } cpu_exec_exit(cpu); return ret; }

主循环的核心步骤包括:异常处理、中断处理、基本块查找与执行。每次循环都会检查退出请求,确保外部事件(如调试器、停止请求)能及时响应。

5. 基本块查找与翻译

QEMU 以基本块(TranslationBlock,TB)为单位进行翻译与缓存。基本块通常以跳转指令或分支指令结尾。查找流程如下:

static TranslationBlock *tb_find(CPUState *cpu, uintptr_t last_tb, int tb_exit) { TranslationBlock *tb; CPUClass *cc = CPU_GET_CLASS(cpu); target_ulong cs_base, pc; uint32_t flags; /* 获取当前 PC 与状态标志 */ cc->get_pc_and_flags(cpu, &pc, &cs_base, &flags); /* 在哈希表中查找已翻译的基本块 */ tb = tb_lookup(cpu, pc, cs_base, flags); if (tb == NULL) { /* 未命中,进行翻译 */ tb = tb_gen_code(cpu, pc, cs_base, flags); } return tb; }

翻译过程由tb_gen_code完成,其内部调用目标架构的翻译函数,将客户机指令逐条转换为 TCG 中间表示(IR),再由 TCG 后端优化并生成主机指令。

6. TCG 翻译流程

TCG 翻译的核心流程如下:

TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc, target_ulong cs_base, uint32_t flags) { TranslationBlock *tb; TCGContext *tcg_ctx = &tcg_ctx; target_ulong next_tb; /* 分配新的 TB 并初始化 */ tb = tb_alloc(pc); if (unlikely(!tb)) { /* 缓存已满,刷新后重试 */ tb_flush(cpu); tb = tb_alloc(pc); } /* 初始化翻译上下文 */ tcg_ctx->tb = tb; tcg_ctx->cpu = cpu; tcg_ctx->gen_opc_ptr = tcg_ctx->gen_opc_buf; tcg_ctx->gen_opparam_ptr = tcg_ctx->gen_opparam_buf; /* 调用目标架构的翻译入口 */ cpu->cc->tcg_initialize(); next_tb = cpu->cc->tb_start(cpu, tb, cs_base, pc, flags); /* 生成 TCG IR */ cpu->cc->translate(cpu, tb, pc, cs_base, flags); /* 结束翻译,生成主机代码 */ tcg_gen_code(tcg_ctx, tb); /* 将 TB 插入哈希表 */ tb_add(tb); return tb; }

其中translate回调由各架构实现,例如 x86 的translate.c、ARM 的translate.c。翻译器逐条解码客户机指令,生成对应的 TCG 操作。

7. 主机代码执行

翻译完成后,QEMU 通过cpu_tb_exec执行生成的主机代码:

static inline void cpu_tb_exec(CPUState *cpu, TranslationBlock *tb) { uintptr_t ret; /* 记录当前执行的 TB,用于调试与异常回溯 */ cpu->current_tb = tb; /* 执行翻译后的代码 */ ret = tcg_qemu_tb_exec(cpu, tb->tc.ptr); /* 根据返回值处理退出原因 */ switch (ret) { case TB_EXIT_REQUESTED: /* 外部请求退出 */ cpu->exit_request = 1; break; case TB_EXIT_ICOUNT_EXPIRED: /* 指令计数到期 */ cpu->icount_decr.u16.low = 0; break; default: /* 正常跳转,更新 PC */ break; } cpu->current_tb = NULL; }

tcg_qemu_tb_exec是 TCG 生成的代码入口,执行完毕后返回退出原因,主循环据此决定继续执行还是处理事件。

8. 异常与中断处理

异常与中断是 CPU 执行的重要组成部分。QEMU 在每次循环中检查异常与中断请求:

static int cpu_handle_exception(CPUState *cpu, int *ret) { CPUClass *cc = CPU_GET_CLASS(cpu); int exception_index = cpu->exception_index; if (exception_index >= 0) { /* 调用架构相关的异常处理 */ if (cc->handle_cpu_exception) { *ret = cc->handle_cpu_exception(cpu, exception_index); } else { *ret = -1; } /* 异常已处理,清除标志 */ cpu->exception_index = -1; return 1; } return 0; }

中断处理类似,通过检查cpu->interrupt_request标志位,调用架构相关的cpu_handle_interrupt回调。

9. 总结

QEMU 的 CPU 执行流程可以概括为以下几个关键环节:

  1. 主循环调度:每个 vCPU 线程运行cpu_exec主循环,持续处理异常、中断与基本块执行。
  2. 基本块缓存:以 TB 为单位缓存翻译结果,通过哈希表加速查找。
  3. 动态翻译:TCG 将客户机指令翻译为 TCG IR,再优化生成主机指令。
  4. 执行与退出:执行主机代码后根据退出原因决定继续执行或处理外部事件。

理解这条主线后,再深入各架构的翻译实现、TCG 优化器以及 KVM 加速路径,就能逐步构建起对 QEMU 整体执行引擎的完整认识。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 5:17:05

Zemax光学设计入门:从指标定义到公差分析的完整验证流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 5:13:06

企业内部流程管理系统部署与实战:72-Skill技术部审批流程详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 5:11:41

AGENT学习-8.向量数据库

向量数据库(Vector Database) 概念 ai用的数据库就是向量数据库 向量数据库(Vector Database)是一种专门用于存储、索引和检索高维向量数据的数据库系统。 传统数据库通过精确匹配来查询(WHERE name Alice&#xff…

作者头像 李华
网站建设 2026/9/6 5:10:19

CMS80F261B uart rx 超时分帧

/****************************************************************************** 文件名称: uart_frame.c* 功能描述: CMS80F261B 串口超时分帧接收模块(精简版)* 说明:* - 使用 UART1 接收数据,Timer0 产生 100us 时基用于超时判断* - 帧结束条件: 接收到数据后,在…

作者头像 李华
网站建设 2026/9/6 5:10:04

电子光栅实验:5GHz热点连不上?聊聊 2.4G 与 5GHz WiFi 的底层逻辑

在做电子光栅实验时,实验基于华为Talent实训平台开展,设备接入网络后就能在云端查看实时运行状态与采集数据。实验室没有覆盖校园WiFi的情况下,手机开热点成了最便捷的组网方式,却没想到栽在了WiFi频段上——默认5GHz的热点死活连…

作者头像 李华
网站建设 2026/9/6 5:09:13

想找专业的算力和服务器测试用接插件公司?口碑好的都在这!

在高端制造检测环节,制造厂商面临诸多痛点。传统人工对接模式效率低下,大型工件人工对位锁紧耗时久,导致产能无法提升且人力成本增加;多机型柔性生产适配难,普通工装通用性差,频繁换产打乱生产计划&#xf…

作者头像 李华