【QEMU源码】cpu_exec_loop实现
cpu_exec_loop函数流程概述
cpu_exec_loop是TCG执行的主要循环函数,负责处理异常、中断和TB(Translation Block)的查找执行循环
1. 异常处理外层循环
函数首先进入异常处理循环,通过cpu_handle_exception检查是否有待处理的异常
cpu-exec.c:921
/* if an exception is pending, we execute it here */
while (!cpu_handle_exception(cpu, &ret))
如果有异常需要处理,函数会退出并返回相应的异常码
cpu-exec.c:686-749
static inline bool cpu_handle_exception(CPUState *cpu, int *ret) {}
2. 中断处理内层循环
在异常处理循环内部,函数进入中断处理循环,通过cpu_handle_interrupt检查中断请求
cpu-exec.c:925
while (!cpu_handle_interrupt(cpu, &last_tb)){
}
中断处理函数会检查各种中断标志,包括调试中断、复位中断等
cpu-exec.c:764-865
static inline bool cpu_handle_interrupt(CPUState *cpu,
TranslationBlock **last_tb) {
}
3. TB预处理
在中断处理循环内部,函数执行核心的TB查找和执行逻辑:
获取CPU状态:通过get_tb_cpu_state获取当前CPU状态,包括PC、CS基址、标志位等
cpu-exec.c:927-928 TranslationBlock *tb; TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); s.cflags = cpu->cflags_next_tb;
处理编译标志:检查是否有特定的编译标志需要应用,如单步调试、指令计数等
cpu-exec.c:937-941
/*
* When requested, use an exact setting for cflags for the next
* execution. This is used for icount, precise smc, and stop-
* after-access watchpoints. Since this request should never
* have CF_INVALID set, -1 is a convenient invalid value that
* does not require tcg headers for cpu_common_reset.
*/
if (s.cflags == -1) {
s.cflags = curr_cflags(cpu);
} else {
cpu->cflags_next_tb = -1;
}
断点检查:调用check_for_breakpoints检查是否命中断点
cpu-exec.c:943-945
if (check_for_breakpoints(cpu, s.pc, &s.cflags)) {
break;
}
4. TB查找或生成
TB查找:使用tb_lookup函数在缓存中查找现有的TB
cpu-exec.c:947 tb = tb_lookup(cpu, s);
TB生成:如果TB不存在,调用tb_gen_code生成新的TB
cpu-exec.c:948-954
if (tb == NULL) {
CPUJumpCache *jc;
uint32_t h;
mmap_lock();
tb = tb_gen_code(cpu, s);
mmap_unlock();
...
}
缓存更新:将新生成的TB添加到跳转缓存中
cpu-exec.c:960-964
if (tb == NULL) {
...
/*
* We add the TB in the virtual pc hash table
* for the fast lookup
*/
h = tb_jmp_cache_hash_func(s.pc);
jc = cpu->tb_jmp_cache;
jc->array[h].pc = s.pc;
qatomic_set(&jc->array[h].tb, tb);
}
5. TB链接优化
跨页检查:在系统模式下,检查TB是否跨越页面边界,如果是则不能建立直接链接
cpu-exec.c:973-976
if (tb_page_addr1(tb) != -1) {
last_tb = NULL;
}
TB链接:如果存在上一个TB且满足条件,调用tb_add_jump建立直接跳转链接
cpu-exec.c:978-980
/* See if we can patch the calling TB. */
if (last_tb) {
tb_add_jump(last_tb, tb_exit, tb);
}
6. TB执行
执行TB:
调用cpu_loop_exec_tb执行当前TB cpu-exec.c:982 。
该函数内部会调用cpu_tb_exec执行实际的机器码 cpu-exec.c:871-872 。
时钟同步:执行完TB后,调用align_clocks进行时钟同步
cpu-exec.c:986
/* Try to align the host and virtual clocks
if the guest is in advance */
align_clocks(sc, cpu);
7. 指令计数处理
在cpu_loop_exec_tb函数中,还会处理指令计数相关的逻辑:
指令计数检查:如果启用了指令计数,会检查是否达到指令限制 cpu-exec.c:891 。
计数器更新:更新指令计数器并重新填充 cpu-exec.c:894-899 。
动态标志调整:根据剩余指令数动态调整编译标志 cpu-exec.c:905-909 。