[RT thread learning notes] how to exit QEMU simulator gracefully

Keywords: Programmer Embedded system Operating System IoT Open Source

This article was originally published by RT thread forum user @ recan: https://club.rt-thread.org/as...
1 problem scenario

I believe many people, like me, have just come into contact with RT thread and are on the way to learn RT thread. However, learning an embedded real-time operating system without a hardware development board, in my previous understanding, it should be difficult to debug the RTOS kernel code?

Until I learned about RT thread, I didn't know that there was such a thing as QEMU simulator.

So I quickly referred to the relevant tutorials and installed QEMU. Combined with the method of compiling bsp with RT thread, the qemu-vexpress-a9 firmware I selected was compiled soon.

After reading the bsp directory, there are several startup scripts:

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

I tried one by one and found that only. / QEMU nographic.sh could run in my environment.

bsp/qemu-vexpress-a9$ ./qemu-nographic.sh
qemu-system-arm: -no-quit is only valid for GTK and SDL, ignoring option
WARNING: Image format was not specified for 'sd.bin' and probing guessed raw.

     Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
     Specify the 'raw' format explicitly to remove the restrictions.

ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
alsa: Could not initialize DAC
alsa: Failed to open `default':
alsa: Reason: No such file or directory
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
alsa: Could not initialize DAC
alsa: Failed to open `default':
alsa: Reason: No such file or directory
audio: Failed to create voice `lm4549.out'

\ | /

  • RT - Thread Operating System
    / | \ 4.0.4 build Nov 5 2021
    2006 - 2021 Copyright by rt-thread team
    lwIP-2.1.2 initialized!
    [I/sal.skt] Socket Abstraction Layer initialize success.
    [I/SDIO] SD card capacity 65536 KB.
    [I/SDIO] switching card to high speed failed!
    hello rt-thread 99, 99
    1, 2
    1, 2
    1, 2
    msh />

But the problem comes. I want to recompile the source code and run the new code again. What should I do? How can I exit the QEMU command line console?
2 try to solve
2.1 trial

As we all know, Linux quits a program started on the console and can exit it by using CTRL+C. I tried and found that it doesn't recognize CTRL+C at all, but always outputs some garbled symbols.
Insert picture description here
2.2 my amplification

Since CTRL+C can't, can I always use kill - 9 XXX? Can you escape the control of the Linux kernel?

So open another console and directly kill - 9 QEMU system arm. As a result, you can exit QEMU (even the process).

But the problem comes. After exiting QEMU, the console feels disorderly. When I look back, it doesn't change lines. Look!
Insert picture description here
This is very uncomfortable. The console can't be used, and you can't echo the command when you hit it. I don't know if you hit it correctly, so you have to exit the command line and log in again, and the console can be restored.
Insert picture description here
2.3 poor skills

The above situation shows that I can't accept it. I thought about it for a moment. QEMU can't not support quitting. Is there any startup parameter I made a mistake, so qemu-system-arm -h found several parameters that seem to be related to this problem:

qemu-system-arm -h
...
-no-quit disable SDL window close capability
...
-no-reboot exit instead of rebooting
...
-no-shutdown stop before shutdown

So add qemu-nographic.sh to try:

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

After running, you can also use kill - 9 QEMU system arm to exit from another console. It is found that sometimes the console that exits QEMU can be well, and sometimes the line feed problem still exists. If you don't find the law, there is no way, so it's over.
3 Ultimate Solution
3.1 discovery of the new world

Until today, when I accidentally turned to the official document of RT thread and introduced the RT thread smart version, there was a chapter about code debugging and running using QEMU simulation environment, which actually mentioned how to exit QEMU!
Insert picture description here
Oh, my God, it's like discovering a new world.
Immediately log in to QEMU development environment for testing. Sure enough, the operation is so smooth and cool!
Insert picture description here
It's really like the feeling that historical problems have been solved.
3.2 who let QEMU quit

The first feeling is whether the find component of RT thread handles CTRL+A,X?
So I found the key code of fish:

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.\n");
    }
}
/* 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("\b");
                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 == '\0' || ch == 0xFF) continue;
    /* handle tab key */
    else if (ch == '\t')
    {
        int i;
        /* move the cursor to the beginning of line */
        for (i = 0; i < shell->line_curpos; i++)
            rt_kprintf("\b");

        /* auto complete */
        shell_auto_complete(&shell->line[0]);
        /* re-calculate position */
        shell->line_curpos = shell->line_position = strlen(shell->line);

        continue;
    }
    /* handle backspace key */
    else if (ch == 0x7f || ch == 0x08)
    {
        /* note that shell->line_curpos >= 0 */
        if (shell->line_curpos == 0)
            continue;

        shell->line_position--;
        shell->line_curpos--;

        if (shell->line_position > shell->line_curpos)
        {
            int i;

            rt_memmove(&shell->line[shell->line_curpos],
                       &shell->line[shell->line_curpos + 1],
                       shell->line_position - shell->line_curpos);
            shell->line[shell->line_position] = 0;

            rt_kprintf("\b%s  \b", &shell->line[shell->line_curpos]);

            /* move the cursor to the origin position */
            for (i = shell->line_curpos; i <= shell->line_position; i++)
                rt_kprintf("\b");
        }
        else
        {
            rt_kprintf("\b \b");
            shell->line[shell->line_position] = 0;
        }

        continue;
    }

    /* handle end of line, break */
    if (ch == '\r' || ch == '\n')
    {

ifdef FINSH_USING_HISTORY

        shell_push_history(shell);

endif

        if (shell->echo_mode)
            rt_kprintf("\n");
        msh_exec(shell->line, shell->line_position);

        rt_kprintf(FINSH_PROMPT);
        memset(shell->line, 0, sizeof(shell->line));
        shell->line_curpos = shell->line_position = 0;
        continue;
    }

    /* it's a large line, discard it */
    if (shell->line_position >= FINSH_CMD_SIZE)
        shell->line_position = 0;

    /* normal character */
    if (shell->line_curpos < shell->line_position)
    {
        int i;

        rt_memmove(&shell->line[shell->line_curpos + 1],
                   &shell->line[shell->line_curpos],
                   shell->line_position - shell->line_curpos);
        shell->line[shell->line_curpos] = ch;
        if (shell->echo_mode)
            rt_kprintf("%s", &shell->line[shell->line_curpos]);

        /* move the cursor to new position */
        for (i = shell->line_curpos; i < shell->line_position; i++)
            rt_kprintf("\b");
    }
    else
    {
        shell->line[shell->line_position] = ch;
        if (shell->echo_mode)
            rt_kprintf("%c", ch);
    }

    ch = 0;
    shell->line_position ++;
    shell->line_curpos++;
    if (shell->line_position >= FINSH_CMD_SIZE)
    {
        /* clear command line */
        shell->line_position = 0;
        shell->line_curpos = 0;
    }
} /* end of device read */

}

After reading through the code, I found that it did not process the CTRL+A,X input. So who took over the instruction?
When I saw QEMU exit, there was a prompt ` `. This keyword gave me a clue, so I began to suspect that QEMU took over the command, so the following operation finally pulled it out.

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

The general process is to grep the executable QEMU system arm and find that the Key log of Terminated is found, which proves that the log exiting this line is being typed out by QEMU system arm. This also proves that the exit command is taken over and processed by QEMU system arm, and then exits.
Insert picture description here
4. Lessons learned

This question really troubled me for at least 2 months. Every time I used QEMU, I make complaints about it. I never thought that RT-Thread's guidance document saved me.

Therefore, if you first check the problems that others have sorted out, you will really get twice the result with half the effort!

Old fellow, RT-Thread's document center, give me a raise!!!
5 more sharing

You are welcome to pay attention to my RT thread forum home page and subscribe to my CSDN column RT thread learning notes. You may be inspired by sharing some practical skills of embedded development and notes on learning RT thread?

Posted by Shovinus on Thu, 11 Nov 2021 02:17:56 -0800