1 问题场景
相信很多人也跟我一样,刚接触RT-Thread不久,正在学习RT-Thread的路上,然而学习一款嵌入式实时操作系统,没有一个硬件开发板,在我之前的认知里面,这应该很难把RTOS的内核代码调试起来吧?
直到了解了RT-Thread,我才知道原来有QEMU模拟器这么个东西。
所以我很快就参考相关教程,把QEMU给装起来了,结合RT-Thread编译bsp的方法,很快我选择的qemu-vexpress-a9固件很快就编译出来了。
看了bsp目录下有好几个启动脚本:
我逐个尝试,发现在我的环境下,只有./qemu-nographic.sh能够跑起来。
不过问题来了,我想重新编译源码,再次运行新的代码,怎么办呢?如何才能退出这个QEMU命令行控制台?
2 尝试解决
2.1 牛刀小试
大家都知道,Linux退出一个控制台启动的程序,使用CTRL+C就可以把它退出来,我试了一下,发现它压根就不认CTRL+C,只是一直输出一些乱码符号。
2.2 我放大招
既然CTRL+C不能,那我用killall -9 xxx总可以吧?难不成你还能逃脱Linux内核对你的管控? 于是另开一个控制台,直接killall -9 qemu-system-arm ,结果一试,的确可以退出QEMU(连进程都退出来了)。
但是问题来了,退出QEMU之后,这个控制台感觉乱来了,我一瞧回车,它都不好好换行了,你看看!
这就很让人难受了,控制台没法用了,而且这个时候敲命令进去还不能回显,也不知道你敲对了没有,只好退出命令行,重新登入,控制台得以恢复。
2.3 黔驴技穷
上面的这种情况,显示是我不能接受的,这个我倒是想了一下,QEMU不可能不支持退出吧,会不会什么启动参数我搞错了,于是qemu-system-arm -h,找了几个看似跟这个问题相关的参数:
qemu-system-arm -h ... -no-quit disable SDL window close capability ... -no-reboot exit instead of rebooting ... -no-shutdown stop before shutdown
于是在qemu-nographic.sh添加来尝试:
if [ ! -f "sd.bin" ]; then dd if=/dev/zero of=sd.bin bs=1024 count=65536 fi qemu-system-arm -M vexpress-a9 -smp cpus=2 -kernel rtthread.bin -nographic -sd sd.bin -no-shutdown -no-quit -no-reboot
运行之后,同样在另一个控制台使用killall -9 qemu-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的关键代码:
void finsh_thread_entry(void *parameter) { int ch; /* normal is echo mode */ #ifndef FINSH_ECHO_DISABLE_DEFAULT shell->echo_mode = 1; #else shell->echo_mode = 0; #endif #if !defined(RT_USING_POSIX) && defined(RT_USING_DEVICE) /* set console device as shell device */ if (shell->device == RT_NULL) { rt_device_t console = rt_console_get_device(); if (console) { finsh_set_device(console->parent.name); } } #endif #ifdef FINSH_USING_AUTH /* set the default password when the password isn't setting */ if (rt_strlen(finsh_get_password()) == 0) { if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK) { rt_kprintf("Finsh password set failed.
"); } } /* waiting authenticate success */ finsh_wait_auth(); #endif rt_kprintf(FINSH_PROMPT); while (1) { ch = (int)finsh_getchar(); if (ch < 0) { continue; } /* * handle control key * up key : 0x1b 0x5b 0x41 * down key: 0x1b 0x5b 0x42 * right key:0x1b 0x5b 0x43 * left key: 0x1b 0x5b 0x44 */ if (ch == 0x1b) { shell->stat = WAIT_SPEC_KEY; continue; } else if (shell->stat == WAIT_SPEC_KEY) { if (ch == 0x5b) { shell->stat = WAIT_FUNC_KEY; continue; } shell->stat = WAIT_NORMAL; } else if (shell->stat == WAIT_FUNC_KEY) { shell->stat = WAIT_NORMAL; if (ch == 0x41) /* up key */ { #ifdef FINSH_USING_HISTORY /* prev history */ if (shell->current_history > 0) shell->current_history --; else { shell->current_history = 0; continue; } /* copy the history command */ memcpy(shell->line, &shell->cmd_history[shell->current_history][0], FINSH_CMD_SIZE); shell->line_curpos = shell->line_position = strlen(shell->line); shell_handle_history(shell); #endif continue; } else if (ch == 0x42) /* down key */ { #ifdef FINSH_USING_HISTORY /* next history */ if (shell->current_history < shell->history_count - 1) shell->current_history ++; else { /* set to the end of history */ if (shell->history_count != 0) shell->current_history = shell->history_count - 1; else continue; } memcpy(shell->line, &shell->cmd_history[shell->current_history][0], FINSH_CMD_SIZE); shell->line_curpos = shell->line_position = strlen(shell->line); shell_handle_history(shell); #endif continue; } else if (ch == 0x44) /* left key */ { if (shell->line_curpos) { rt_kprintf(""); shell->line_curpos --; } continue; } else if (ch == 0x43) /* right key */ { if (shell->line_curpos < shell->line_position) { rt_kprintf("%c", shell->line[shell->line_curpos]); shell->line_curpos ++; } continue; } } /* received null or error */ if (ch == ' |