qemu是一个支持跨平台的虚拟机,其使用分两种方式,分别是system mode和user mode。在qemu的system mode的配置下可以
模拟出来整个计算机。从源码的路径看user mode 根据架构的不同有分为bsd-user和linux-user,这里以linux-user为例看看
user mode具体作用是啥?
linux-user mode的的源码在linux-user中,从源码中可以看到linux-user mode支持的要模拟的架构,例如aarch64/alpha 等
[root@localhost linux-user]# ls -al
total 1100
drwxr-xr-x. 25 root root 4096 Jun 11 02:42 .
drwxr-xr-x. 57 root root 4096 Jun 11 03:02 ..
-rw-r--r--. 1 root root 791 Jun 11 02:42 Makefile.objs
drwxr-xr-x. 2 root root 229 Jun 11 02:42 aarch64
drwxr-xr-x. 2 root root 270 Jun 11 02:42 alpha
drwxr-xr-x. 3 root root 4096 Jun 11 02:42 arm
-rw-r--r--. 1 root root 1573 Jun 11 02:42 cpu_loop-common.h
drwxr-xr-x. 2 root root 229 Jun 11 02:42 cris
-rw-r--r--. 1 root root 116436 Jun 11 02:42 elfload.c
-rw-r--r--. 1 root root 10124 Jun 11 02:42 errno_defs.h
-rw-r--r--. 1 root root 1118 Jun 11 02:42 exit.c
-rw-r--r--. 1 root root 44353 Jun 11 02:42 fd-trans.c
-rw-r--r--. 1 root root 3084 Jun 11 02:42 fd-trans.h
-rw-r--r--. 1 root root 2607 Jun 11 02:42 flat.h
-rw-r--r--. 1 root root 26124 Jun 11 02:42 flatload.c
drwxr-xr-x. 2 root root 55 Jun 11 02:42 generic
drwxr-xr-x. 17 root root 200 Jun 11 02:42 host
drwxr-xr-x. 2 root root 270 Jun 11 02:42 hppa
drwxr-xr-x. 2 root root 273 Jun 11 02:42 i386
-rw-r--r--. 1 root root 27359 Jun 11 02:42 ioctls.h
-rw-r--r--. 1 root root 2696 Jun 11 02:42 linux_loop.h
-rw-r--r--. 1 root root 4099 Jun 11 02:42 linuxload.c
drwxr-xr-x. 2 root root 270 Jun 11 02:42 m68k
-rw-r--r--. 1 root root 24711 Jun 11 02:42 main.c
drwxr-xr-x. 2 root root 270 Jun 11 02:42 microblaze
[root@localhost linux-user]#
linux-user 最终会编译成qemu-user 和 qemu-user-static 两个binary,
从makefile中可以看到入口函数在main.c中
obj-y = main.o syscall.o strace.o mmap.o signal.o \
elfload.o linuxload.o uaccess.o uname.o \
safe-syscall.o $(TARGET_ABI_DIR)/signal.o \
$(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o
其入口函数在main
int main(int argc, char **argv, char **envp)
{
#tcg的初始化,可见user mode 也是用tcg 来翻译代码的。
/* init tcg before creating CPUs and to get qemu_host_page_size */
tcg_exec_init(0);
cpu = cpu_create(cpu_type);
env = cpu->env_ptr;
cpu_reset(cpu);
thread_cpu = cpu;
/*
* Prepare copy of argv vector for target.
*/
target_argc = argc - optind;
target_argv = calloc(target_argc + 1, sizeof (char *));
if (target_argv == NULL) {
(void) fprintf(stderr, "Unable to allocate memory for target_argv\n");
exit(EXIT_FAILURE);
}
/*
* If argv0 is specified (using '-0' switch) we replace
* argv[0] pointer with the given one.
*/
i = 0;
if (argv0 != NULL) {
target_argv[i++] = strdup(argv0);
}
for (; i < target_argc; i++) {
target_argv[i] = strdup(argv[optind + i]);
}
target_argv[target_argc] = NULL;
ts = g_new0(TaskState, 1);
init_task_state(ts);
/* build Task State */
ts->info = info;
ts->bprm = &bprm;
cpu->opaque = ts;
task_settid(ts);
#加载要执行的程序,即要执行的guest code
ret = loader_exec(execfd, exec_path, target_argv, target_environ, regs,
info, &bprm);
if (ret != 0) {
printf("Error while loading %s: %s\n", exec_path, strerror(-ret));
_exit(EXIT_FAILURE);
}
for (wrk = target_environ; *wrk; wrk++) {
g_free(*wrk);
}
g_free(target_environ);
target_set_brk(info->brk);
syscall_init();
signal_init();
/* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
generating the prologue until now so that the prologue can take
the real value of GUEST_BASE into account. */
tcg_prologue_init(tcg_ctx);
tcg_region_init();
target_cpu_copy_regs(env, regs);
if (gdbstub) {
if (gdbserver_start(gdbstub) < 0) {
fprintf(stderr, "qemu: could not open gdbserver on %s\n",
gdbstub);
exit(EXIT_FAILURE);
}
gdb_handlesig(cpu, 0);
}
开始执行guest code 翻译成host code执行,其中首先通过translate-all.c中的gen_intermediate_code讲guest code
翻译成TCG Operation,然后通过调用tcg/tfg.c 中的tcg_gen_code将TCG Operation 转成host code,执行.
cpu_loop(env);
/* never exits */
return 0;
}