qemu之linux-user备忘
2021/12/27 7:12:43
本文主要是介绍qemu之linux-user备忘,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
最后更新2021/12/26
这几天研究qemu linux-user有关的问题,此地立个桩,把我认为值得记录的研究结果记录在此。qemu整体的编译不细说了,只针对我需要的ppc64相关关键记录,很大众特别是没遇到问题的过程就不记录了,也许以后整理全本说明的时候填充一下。另外由于我的最终目标是搞aix-user,与aix-user相关的内容也许会在这里有关联,但全部的内容应在另一个专题中,此处都是linux-user的东西。
前置
通过configure定义目标,我用的是:
./configure --target-list="ppc64-softmmu ppc64-linux-user" --disable-curses --disable-kvm --disable-sdl
除了disable-curses是为了填wsl上libtinfow.so.6 missing的老坑,别的随意。
然后make,如果一切正常,会搞出两个可执行文件,一个用于虚拟ppc64 machine,另一个可作为ppc64 linux elf程序的加载器:
ppc64-softmmu/qemu-system-ppc64 ppc64-linux-user/qemu-ppc64
本系列只研究linux-user。
顺序结构
主代码在linux-user/main.c
从main()开始,都是各种初始化,涉及到的时候会更新本文。我喜欢直达关键点,其它的东西遇到再说。虽然这种方法不太适合一层层抽丝剥茧,特别是没有任何概念的时候,一下子跳得太远,会乱。但对我现在来说,如果走太多岔路,最后都不知道转到哪里去了。以走迷宫比喻,一种方法是从入口遍历每个岔路;我喜欢先到出口,反向走逆推。所以,我们就直接翻到main.c的最后几行:
cpu_loop(env);
cpu_loop里面是个死循环
到了cpu_loop,则所有初始化等准备工作都已经完成,包括待加载执行文件格式审核、加载到内存等等,马上需要做的只是解码执行了。
在很多文件中都有cpu_loop调用,真正被使用的是对应目标cpu架构下的cpu_loop,我这里用的是linux-user/ppc/cpu_loop.c
cpu_loop()看起来也很简单,进来就是个for死循环,在里面完成这么几个操作:
for (;;) { cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); process_queued_cpu_work(cs); ... switch (trapnr) { case POWERPC_EXCP_NONE: ... } process_pending_signals(env); }
这里有个背景知识,qemu执行目标代码是以jump block为单位,每个jump block(可能不是这个专用词,以后更新)是一组被连续执行的代码,直到需要跳转到其它地址或者发生意外中断等等情况。
那么,这个结构就很清晰了,代码从cs指定位置开始做准备,(解码、生成tcg代码等等,注:其细节待确认,具体解码在cpu_exec_start还是在cpu_exec)解码顺序执行,直到本块执行完或者发生意外(此意外包含所有意外还是只包含guest代码意外待确认),返回后,进行块后处理,此块将被保留,下次再跑到这个地址,就无需解码,更快了。
这里有个潜在的情况,就是代码块的连续性,会存在代码块解码时是连续的,运行时会提前中断跳出,或者虽然是跳转,本应两个代码块,但前后两个代码块其实可以合并到一起。这些问题qemu都有考虑,后续深入分析的时候能开大相应的处理。
代码块后处理完成后就是意外处理,一个switc,case一堆的意外类型。这里也是我要关注的syscall问题。
case POWERPC_EXCP_SYSCALL_USER: /* system call in user-mode emulation */ /* WARNING: * PPC ABI uses overflow flag in cr0 to signal an error * in syscalls. */ env->crf[0] &= ~0x1; env->nip += 4; ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6], env->gpr[7], env->gpr[8], 0, 0); if (ret == -TARGET_ERESTARTSYS) { env->nip -= 4; break; }
至此,那基本确定了linux-user是通过生成syscall意外,由guest代码中断来,转到host代码执行syscall的。下一步要看看如何触发syscall意外。
在switch case循环里其实还有另一个syscall意外:
case POWERPC_EXCP_SYSCALL: /* System call exception */ cpu_abort(cs, "Syscall exception while in user mode. " "Aborting\n"); break;
看起来这个syscall意外是”真正“的syscall时发生意外,而不是用于跳转代码的,以后再分析。
继续cpu_exec_start(cs)
cpu_exec_start(cs)在cpus_common.c,
-
进来首先搞个原子操作: atomic_set(&cpu->running, true); 可以理解,如果两个线程搞到一起,同时执行(解码)这块代码,肯定冲突啊!所以先上锁。如果以后不需要解码,应该就没这个问题,待确认,我想对应的代码块也会被扩展合并。具体如何慢慢研究。
-
再下面是smp_mb(),据说是控制内存一致性的,让编译器不要跨区域把内存读写搞乱。具体为何要放在这,怎么生效的先不管,至少不影响执行逻辑。
-
然后是个atom_read,果然需要读内存,还要原子读,而且是需要有空闲cpu(guest)才允许读。其实,我觉得如果为了性能考虑,这里可以优化一下,不去管guest是否有空闲cpu。当然,本身linux-user不是为性能而设计,只是为了虚拟,而且需要控制,分配个guest多少cpu,与这个guest相关的操作都应该在这些cpu之内,这么设计也可以理解。
好了,cpu_exec_start返回了。至此,说明cpu_exec_start啥都没干,只是把一部分代码读入,前面所说的解码什么都,都不在这里。具体atom_read是怎么个原子读,以后再看。
主菜cpu_exec(cs)
累了,休息一会。。。
饭后甜点cpu_exec_end(cs)
各种意外处理
POWERPC_EXCP_SYSCALL
关于各种意外的处理。。。待续
各种意外处理完,该abort的abort,该继续的继续,又会返回for循环,开始下一个代码块处理。
随机发现、确认
- guest代码在被执行时,通过生成syscall意外跳出guest代码,返回host代码,触发执行host syscall调用;
反思
这篇关于qemu之linux-user备忘的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23linux 系统宝塔查看网站访问的命令是什么?-icode9专业技术文章分享
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南