0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【RT-Thread学习笔记】如何优雅地退出QEMU模拟器?

嵌入式物联网开发 2022-07-26 04:06 次阅读

1 问题场景

相信很多人也跟我一样,刚接触RT-Thread不久,正在学习RT-Thread的路上,然而学习一款嵌入式实时操作系统,没有一个硬件开发板,在我之前的认知里面,这应该很难把RTOS的内核代码调试起来吧?

直到了解了RT-Thread,我才知道原来有QEMU模拟器这么个东西。

所以我很快就参考相关教程,把QEMU给装起来了,结合RT-Thread编译bsp的方法,很快我选择的qemu-vexpress-a9固件很快就编译出来了。

看了bsp目录下有好几个启动脚本:

  1. bsp/qemu-vexpress-a9$ ls -al *.sh
  2. -rwxr-xr-x 1 recan system 168 Sep 6 10:43 qemu-dbg.sh
  3. -rwxr-xr-x 1 recan system 187 Oct 22 17:41 qemu-nographic.sh
  4. -rwxr-xr-x 1 recan system 166 Sep 6 10:43 qemu.sh

我逐个尝试,发现在我的环境下,只有./qemu-nographic.sh能够跑起来。

  1. bsp/qemu-vexpress-a9$ ./qemu-nographic.sh
  2. qemu-system-arm: -no-quit is only valid for GTK and SDL, ignoring option
  3. WARNING: Image format was not specified for 'sd.bin' and probing guessed raw.
  4. Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
  5. Specify the 'raw' format explicitly to remove the restrictions.
  6. ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
  7. ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
  8. ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
  9. ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
  10. ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
  11. ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
  12. ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
  13. ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
  14. alsa: Could not initialize DAC
  15. alsa: Failed to open `default':
  16. alsa: Reason: No such file or directory
  17. ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
  18. ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
  19. ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
  20. ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
  21. ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
  22. ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
  23. ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
  24. ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
  25. alsa: Could not initialize DAC
  26. alsa: Failed to open `default':
  27. alsa: Reason: No such file or directory
  28. audio: Failed to create voice `lm4549.out'
  29. \ | /
  30. - RT - Thread Operating System
  31. / | \ 4.0.4 build Nov 5 2021
  32. 2006 - 2021 Copyright by rt-thread team
  33. lwIP-2.1.2 initialized!
  34. [I/sal.skt] Socket Abstraction Layer initialize success.
  35. [I/SDIO] SD card capacity 65536 KB.
  36. [I/SDIO] switching card to high speed failed!
  37. hello rt-thread 99, 99
  38. 1, 2
  39. 1, 2
  40. 1, 2
  41. msh />

不过问题来了,我想重新编译源码,再次运行新的代码,怎么办呢?如何才能退出这个QEMU命令行控制台?

2 尝试解决

2.1 牛刀小试

大家都知道,Linux退出一个控制台启动的程序,使用CTRL+C就可以把它退出来,我试了一下,发现它压根就不认CTRL+C,只是一直输出一些乱码符号。

在这里插入图片描述

2.2 我放大招

既然CTRL+C不能,那我用killall-9xxx总可以吧?难不成你还能逃脱Linux内核对你的管控?

于是另开一个控制台,直接killall-9qemu-system-arm,结果一试,的确可以退出QEMU(连进程都退出来了)。

但是问题来了,退出QEMU之后,这个控制台感觉乱来了,我一瞧回车,它都不好好换行了,你看看!

在这里插入图片描述

这就很让人难受了,控制台没法用了,而且这个时候敲命令进去还不能回显,也不知道你敲对了没有,只好退出命令行,重新登入,控制台得以恢复。

在这里插入图片描述

2.3 黔驴技穷

上面的这种情况,显示是我不能接受的,这个我倒是想了一下,QEMU不可能不支持退出吧,会不会什么启动参数我搞错了,于是qemu-system-arm-h,找了几个看似跟这个问题相关的参数:

  1. qemu-system-arm -h
  2. ...
  3. -no-quit disable SDL window close capability
  4. ...
  5. -no-reboot exit instead of rebooting
  6. ...
  7. -no-shutdown stop before shutdown

于是在qemu-nographic.sh添加来尝试:

  1. if [ ! -f "sd.bin" ]; then
  2. dd if=/dev/zero of=sd.bin bs=1024 count=65536
  3. fi
  4. qemu-system-arm -M vexpress-a9 -smp cpus=2 -kernel rtthread.bin -nographic -sd sd.bin -no-shutdown -no-quit -no-reboot

运行之后,同样在另一个控制台使用killall-9qemu-system-arm退出,发现有的时候退出QEMU的控制台可以好好的,有的时候换行问题依然存在,没有找到规律,实在没办法,就不了了之了。

3 终极方案

3.1 发现新大陆

直到今天,我偶然翻到RT-Thread的官方文档,对RT-Thread Smart版本的介绍的时候,有一个章节是介绍使用QEMU模拟环境进行代码调试运行的,里面居然提到了如何退出QEMU!

在这里插入图片描述

Word天呐,那种感觉简直像是发现新大陆一样。 马上登入QEMU开发环境做测试,果然,操作竟是如此的丝滑,就一个字!

在这里插入图片描述

真的像是历史难题被解决的那种感觉。

3.2 扒一扒到底谁让QEMU退出了

第一感觉是不是RT-Thread的Finsh组件处理了这个CTRL+A,X? 于是找了Finsh的关键代码:

  1. void finsh_thread_entry(void *parameter)
  2. {
  3. int ch;
  4. /* normal is echo mode */
  5. #ifndef FINSH_ECHO_DISABLE_DEFAULT
  6. shell->echo_mode = 1;
  7. #else
  8. shell->echo_mode = 0;
  9. #endif
  10. #if !defined(RT_USING_POSIX) && defined(RT_USING_DEVICE)
  11. /* set console device as shell device */
  12. if (shell->device == RT_NULL)
  13. {
  14. rt_device_t console = rt_console_get_device();
  15. if (console)
  16. {
  17. finsh_set_device(console->parent.name);
  18. }
  19. }
  20. #endif
  21. #ifdef FINSH_USING_AUTH
  22. /* set the default password when the password isn't setting */
  23. if (rt_strlen(finsh_get_password()) == 0)
  24. {
  25. if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK)
  26. {
  27. rt_kprintf("Finsh password set failed.\n");
  28. }
  29. }
  30. /* waiting authenticate success */
  31. finsh_wait_auth();
  32. #endif
  33. rt_kprintf(FINSH_PROMPT);
  34. while (1)
  35. {
  36. ch = (int)finsh_getchar();
  37. if (ch < 0)
  38. {
  39. continue;
  40. }
  41. /*
  42. * handle control key
  43. * up key : 0x1b 0x5b 0x41
  44. * down key: 0x1b 0x5b 0x42
  45. * right key:0x1b 0x5b 0x43
  46. * left key: 0x1b 0x5b 0x44
  47. */
  48. if (ch == 0x1b)
  49. {
  50. shell->stat = WAIT_SPEC_KEY;
  51. continue;
  52. }
  53. else if (shell->stat == WAIT_SPEC_KEY)
  54. {
  55. if (ch == 0x5b)
  56. {
  57. shell->stat = WAIT_FUNC_KEY;
  58. continue;
  59. }
  60. shell->stat = WAIT_NORMAL;
  61. }
  62. else if (shell->stat == WAIT_FUNC_KEY)
  63. {
  64. shell->stat = WAIT_NORMAL;
  65. if (ch == 0x41) /* up key */
  66. {
  67. #ifdef FINSH_USING_HISTORY
  68. /* prev history */
  69. if (shell->current_history > 0)
  70. shell->current_history --;
  71. else
  72. {
  73. shell->current_history = 0;
  74. continue;
  75. }
  76. /* copy the history command */
  77. memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  78. FINSH_CMD_SIZE);
  79. shell->line_curpos = shell->line_position = strlen(shell->line);
  80. shell_handle_history(shell);
  81. #endif
  82. continue;
  83. }
  84. else if (ch == 0x42) /* down key */
  85. {
  86. #ifdef FINSH_USING_HISTORY
  87. /* next history */
  88. if (shell->current_history < shell->history_count - 1)
  89. shell->current_history ++;
  90. else
  91. {
  92. /* set to the end of history */
  93. if (shell->history_count != 0)
  94. shell->current_history = shell->history_count - 1;
  95. else
  96. continue;
  97. }
  98. memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  99. FINSH_CMD_SIZE);
  100. shell->line_curpos = shell->line_position = strlen(shell->line);
  101. shell_handle_history(shell);
  102. #endif
  103. continue;
  104. }
  105. else if (ch == 0x44) /* left key */
  106. {
  107. if (shell->line_curpos)
  108. {
  109. rt_kprintf("\b");
  110. shell->line_curpos --;
  111. }
  112. continue;
  113. }
  114. else if (ch == 0x43) /* right key */
  115. {
  116. if (shell->line_curpos < shell->line_position)
  117. {
  118. rt_kprintf("%c", shell->line[shell->line_curpos]);
  119. shell->line_curpos ++;
  120. }
  121. continue;
  122. }
  123. }
  124. /* received null or error */
  125. if (ch == '\0' || ch == 0xFF) continue;
  126. /* handle tab key */
  127. else if (ch == '\t')
  128. {
  129. int i;
  130. /* move the cursor to the beginning of line */
  131. for (i = 0; i < shell->line_curpos; i++)
  132. rt_kprintf("\b");
  133. /* auto complete */
  134. shell_auto_complete(&shell->line[0]);
  135. /* re-calculate position */
  136. shell->line_curpos = shell->line_position = strlen(shell->line);
  137. continue;
  138. }
  139. /* handle backspace key */
  140. else if (ch == 0x7f || ch == 0x08)
  141. {
  142. /* note that shell->line_curpos >= 0 */
  143. if (shell->line_curpos == 0)
  144. continue;
  145. shell->line_position--;
  146. shell->line_curpos--;
  147. if (shell->line_position > shell->line_curpos)
  148. {
  149. int i;
  150. rt_memmove(&shell->line[shell->line_curpos],
  151. &shell->line[shell->line_curpos + 1],
  152. shell->line_position - shell->line_curpos);
  153. shell->line[shell->line_position] = 0;
  154. rt_kprintf("\b%s \b", &shell->line[shell->line_curpos]);
  155. /* move the cursor to the origin position */
  156. for (i = shell->line_curpos; i <= shell->line_position; i++)
  157. rt_kprintf("\b");
  158. }
  159. else
  160. {
  161. rt_kprintf("\b \b");
  162. shell->line[shell->line_position] = 0;
  163. }
  164. continue;
  165. }
  166. /* handle end of line, break */
  167. if (ch == '\r' || ch == '\n')
  168. {
  169. #ifdef FINSH_USING_HISTORY
  170. shell_push_history(shell);
  171. #endif
  172. if (shell->echo_mode)
  173. rt_kprintf("\n");
  174. msh_exec(shell->line, shell->line_position);
  175. rt_kprintf(FINSH_PROMPT);
  176. memset(shell->line, 0, sizeof(shell->line));
  177. shell->line_curpos = shell->line_position = 0;
  178. continue;
  179. }
  180. /* it's a large line, discard it */
  181. if (shell->line_position >= FINSH_CMD_SIZE)
  182. shell->line_position = 0;
  183. /* normal character */
  184. if (shell->line_curpos < shell->line_position)
  185. {
  186. int i;
  187. rt_memmove(&shell->line[shell->line_curpos + 1],
  188. &shell->line[shell->line_curpos],
  189. shell->line_position - shell->line_curpos);
  190. shell->line[shell->line_curpos] = ch;
  191. if (shell->echo_mode)
  192. rt_kprintf("%s", &shell->line[shell->line_curpos]);
  193. /* move the cursor to new position */
  194. for (i = shell->line_curpos; i < shell->line_position; i++)
  195. rt_kprintf("\b");
  196. }
  197. else
  198. {
  199. shell->line[shell->line_position] = ch;
  200. if (shell->echo_mode)
  201. rt_kprintf("%c", ch);
  202. }
  203. ch = 0;
  204. shell->line_position ++;
  205. shell->line_curpos++;
  206. if (shell->line_position >= FINSH_CMD_SIZE)
  207. {
  208. /* clear command line */
  209. shell->line_position = 0;
  210. shell->line_curpos = 0;
  211. }
  212. } /* end of device read */
  213. }

通读代码之后,发现它并没有处理这个CTRL+A,X输入,那么到底是谁接管了这个指令呢? 看到QEMU退出的时候,有提示``,这个关键字给了我线索,于是我开始怀疑是QEMU自己接管的这个命令,于是下面的一顿操作终于把它揪出来了。

  1. bsp/qemu-vexpress-a9$ whereis qemu-system-arm
  2. qemu-system-arm: /usr/bin/qemu-system-arm /usr/share/man/man1/qemu-system-arm.1.gz
  3. bsp/qemu-vexpress-a9$
  4. bsp/qemu-vexpress-a9$ cp /usr/bin/qemu-system-arm .
  5. bsp/qemu-vexpress-a9$
  6. bsp/qemu-vexpress-a9$ grep -rsn "Terminated"
  7. Binary file qemu-system-arm matches
  8. bsp/qemu-vexpress-a9$
  9. bsp/qemu-vexpress-a9$ hexdump -C qemu-system-arm | grep -n "Terminated"
  10. 699798:00b2b4a0 4d 55 3a 20 54 65 72 6d 69 6e 61 74 65 64 0a 0d |MU: Terminated..|
  11. bsp/qemu-vexpress-a9$
  12. bsp/qemu-vexpress-a9$ hexdump -C qemu-system-arm > hexdump.log
  13. bsp/qemu-vexpress-a9$
  14. bsp/qemu-vexpress-a9$ head -699797 hexdump.log | tail -1
  15. 00b2b490 73 20 68 65 6c 70 0a 0d 00 43 2d 25 63 00 51 45 |s help...C-%c.QE|
  16. bsp/qemu-vexpress-a9$
  17. bsp/qemu-vexpress-a9$ head -699798 hexdump.log | tail -1
  18. 00b2b4a0 4d 55 3a 20 54 65 72 6d 69 6e 61 74 65 64 0a 0d |MU: Terminated..|
  19. bsp/qemu-vexpress-a9$

大致的流程就是对可执行文件qemu-system-arm进行grep检索,发现居然找到了Terminated这个关键log,证明这行退出的log正在qemu-system-arm打出来的,这也就从侧面证实了这个退出命令是被它接管了,并且处理了,然后才退出的。

在这里插入图片描述

4 经验教训

这个问题真的困扰了我至少2个月,每次一用QEMU,我就吐槽这个问题,没想到居然还是RT-Thread的指导文档拯救了我。

所以啊,凡事先查查别人已经整理好的问题,真的会事半功倍!

各位老铁,RT-Thread的文档中心,给我撸起来!!!

5 更多分享

架构师李肯

一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获CSDN博客专家、CSDN物联网领域优质创作者、2021年度CSDN&RT-Thread技术社区之星、RT-Thread官方嵌入式开源社区认证专家、RT-Thread 2021年度论坛之星TOP4、华为云云享专家(嵌入式物联网架构设计师)等荣誉。坚信【知识改变命运,技术改变世界】!

欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。

同时也非常欢迎关注我的专栏;有问题的话,可以跟我讨论,知无不答,谢谢大家。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 模拟器
    +关注

    关注

    2

    文章

    838

    浏览量

    42753
  • RT-Thread
    +关注

    关注

    31

    文章

    1176

    浏览量

    38988
  • qemu
    +关注

    关注

    0

    文章

    54

    浏览量

    5240
收藏 人收藏

    评论

    相关推荐

    6月6日杭州站RT-Thread线下workshop,探索RT-Thread混合部署新模式!

    6月6日下午我们将在杭州举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深
    的头像 发表于 05-28 08:35 93次阅读
    6月6日杭州站<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新模式!

    4月25日北京站RT-Thread线下workshop,探索RT-Thread混合部署新模式

    4月25日,下午我们将在北京举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深
    的头像 发表于 04-16 08:35 149次阅读
    4月25日北京站<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新模式

    4月10日深圳场RT-Thread线下workshop,探索RT-Thread混合部署新模式!

    4月10日我们将在深圳福田举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深嵌入式软件工程师农晓明老师为您讲
    的头像 发表于 03-27 11:36 477次阅读
    4月10日深圳场<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新模式!

    4月10日深圳场RT-Thread线下workshop,探索RT-Thread混合部署新模式!

    4月10日我们将在深圳福田举办RT-Thread混合部署线下workshop,在瑞芯微RK3568平台上实现同时运行RT-Thread和linux,本次workshop邀请到RT-Thread资深
    的头像 发表于 03-27 08:34 218次阅读
    4月10日深圳场<b class='flag-5'>RT-Thread</b>线下workshop,探索<b class='flag-5'>RT-Thread</b>混合部署新模式!

    基于rt-thread的socket通信设计

    最近再研究 rt-thread 的通信 ,想设计出 eps8266(多个) rt-thread(作为中控) 服务器的通信框架,使用的开发板是 潘多拉
    的头像 发表于 10-13 15:02 782次阅读
    基于<b class='flag-5'>rt-thread</b>的socket通信设计

    RT-Thread使用Soft RTC(软件模拟RTC)

    开发环境:野火的stm32f407,rt-thread studio版本是版本: 2.2.6,rt-thread 使用版本为4.0.3,stm32f4的资源包为0.2.2。
    的头像 发表于 10-12 17:39 400次阅读
    <b class='flag-5'>RT-Thread</b>使用Soft RTC(软件<b class='flag-5'>模拟</b>RTC)

    试用RT-Thread Studio(VSCode)

    想尝试RT-Thread studio (VSCode),先下载安装VSCode,再搜索RT-Thread
    的头像 发表于 10-12 10:58 614次阅读
    试用<b class='flag-5'>RT-Thread</b> Studio(VSCode)

    RT-Thread v5.0.2 发布

    RT-Thread 代码仓库地址: ●  https://github.com/RT-Thread/rt-thread RT-Thread 5.0.2 版本发布日志详情: ●  htt
    的头像 发表于 10-10 18:45 867次阅读
    <b class='flag-5'>RT-Thread</b> v5.0.2 发布

    RT-Thread BSP qemu-virt64-aarch64文件系统

    前面大体上搭建了 RT-Thread BSP qemu-virt64-aarch64 的交叉编译环境,运行后发现,文件系统没有挂载上,感觉是没有 mkfs。
    的头像 发表于 10-08 16:34 615次阅读
    <b class='flag-5'>RT-Thread</b> BSP <b class='flag-5'>qemu</b>-virt64-aarch64文件系统

    使用RT-Thread Master+QEMU模拟器进行RT-Thread原型快速开发

    前段时间分别在Win和Mac M1/M2 Silicon硬件环境下折腾了VS Code + RT-Thread的编译问题。
    的头像 发表于 09-27 14:50 1993次阅读
    使用<b class='flag-5'>RT-Thread</b> Master+<b class='flag-5'>QEMU</b><b class='flag-5'>模拟器</b>进行<b class='flag-5'>RT-Thread</b>原型快速开发

    rt-thread studio中创建smart工程,使用qemu-vexpress-a9进行编译失败了的原因?

    rt-thread studio中创建smart工程,使用qemu-vexpress-a9进行编译,失败 请问:是不是smart项目还不支持啊?
    发表于 09-07 16:54

    RT-Thread qemu mps2-an385 bsp移植制作 :环境搭建篇

    最近打算系统地研究一下 RT-Thread,包括 RT-Thread BSP 的移植,由于一直在使用 QEMU 进行一些软件功能的验证,qemu 支持很多的CPU 与 开发板,所以想移
    的头像 发表于 08-02 14:45 385次阅读
    <b class='flag-5'>RT-Thread</b> <b class='flag-5'>qemu</b> mps2-an385 bsp移植制作 :环境搭建篇

    教你手上没有开发板如何跑RT-THREAD STM32应用?

    首先打开 RT-Thread Studio,新建RT-Thread
    的头像 发表于 07-18 16:09 1117次阅读
    教你手上没有开发板如何跑<b class='flag-5'>RT-THREAD</b> STM32应用?

    RT-Thread入门学习笔记-熟悉全局中断的操作

    RT-Thread中,全局中断的操作很多,大家都知道全局中断的【disable】与【enable】
    的头像 发表于 06-07 14:58 1162次阅读
    <b class='flag-5'>RT-Thread</b>入门<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>-熟悉全局中断的操作

    RT-Thread内核对象操作API详解

    目的还是学习并熟悉RT-Thread 操作系统。
    发表于 06-02 09:48 354次阅读
    <b class='flag-5'>RT-Thread</b>内核对象操作API详解