其他分享
首页 > 其他分享> > RT-Thread之控制台线程工作流程学习记录

RT-Thread之控制台线程工作流程学习记录

作者:互联网

RT-Thread控制台工作流程

要使用RT-Thread需要在rtconfig.h头文件添加如下宏定义,RT-Thread官方定义的,不想深究这个

/* Command shell */
#define RT_USING_FINSH
#define FINSH_THREAD_NAME "tshell"
#define FINSH_USING_HISTORY
#define FINSH_HISTORY_LINES 5
#define FINSH_USING_SYMTAB
#define FINSH_USING_DESCRIPTION
#define FINSH_THREAD_PRIORITY 20
#define FINSH_THREAD_STACK_SIZE 512
#define FINSH_CMD_SIZE 80
#define FINSH_USING_MSH
#define FINSH_USING_MSH_DEFAULT
#define FINSH_USING_MSH_ONLY

从finsh_system_init(void)开始

...
#ifdef RT_USING_HEAP
    /* create or set shell structure */
    shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell)); //<------创建1个长度为sizeof(struct finsh_shell)的连续空间,返回这个空间的首地址。与malloc不同之处是calloc会把这个空间初始化为0
    if (shell == RT_NULL)
    {
        rt_kprintf("no memory for shell\n");
        return -1;
    }
    tid = rt_thread_create(FINSH_THREAD_NAME,
                           finsh_thread_entry, RT_NULL,
                           FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10);
#else
    shell = &_shell;
    tid = &finsh_thread;
    result = rt_thread_init(&finsh_thread,
                            FINSH_THREAD_NAME,
                            finsh_thread_entry, RT_NULL,
                            &finsh_thread_stack[0], sizeof(finsh_thread_stack),
                            FINSH_THREAD_PRIORITY, 10);
#endif /* RT_USING_HEAP */

    rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);

    if (tid != NULL && result == RT_EOK)
        rt_thread_startup(tid);
...

这里有一个结构体finsh_shell,几乎是最重要的东西了

struct finsh_shell
{
    struct rt_semaphore rx_sem;

    enum input_stat stat;

    rt_uint8_t echo_mode:1;

#ifdef FINSH_USING_HISTORY
    rt_uint16_t current_history;
    rt_uint16_t history_count;

    char cmd_history[FINSH_HISTORY_LINES][FINSH_CMD_SIZE];
#endif

#ifndef FINSH_USING_MSH_ONLY
    struct finsh_parser parser;
#endif

    char line[FINSH_CMD_SIZE];
    rt_uint8_t line_position;
    rt_uint8_t line_curpos;

#ifndef RT_USING_POSIX
    rt_device_t device;
#endif

#ifdef FINSH_USING_AUTH
    char password[FINSH_PASSWORD_MAX];
#endif
};

至此可以进入控制台线程中看看它干了什么

struct finsh_shell *shell;
...
    /* normal is echo mode */
#ifndef FINSH_ECHO_DISABLE_DEFAULT
    shell->echo_mode = 1;
#else
    shell->echo_mode = 0;
#endif

#ifndef FINSH_USING_MSH_ONLY
    finsh_init(&shell->parser);
#endif

#ifndef RT_USING_POSIX
    /* 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
...
rt_device_t dev = RT_NULL;

    RT_ASSERT(shell != RT_NULL);
    dev = rt_device_find(device_name);
    if (dev == RT_NULL)
    {
        rt_kprintf("finsh: can not find device: %s\n", device_name);
        return;
    }

    /* check whether it's a same device */
    if (dev == shell->device) return;
    /* open this device and set the new device in finsh shell */
    if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \
                       RT_DEVICE_FLAG_STREAM) == RT_EOK)
    {
        if (shell->device != RT_NULL)
        {
            /* close old finsh device */
            rt_device_close(shell->device);
            rt_device_set_rx_indicate(shell->device, RT_NULL);
        }

        /* clear line buffer before switch to new device */
        memset(shell->line, 0, sizeof(shell->line));
        shell->line_curpos = shell->line_position = 0;

        shell->device = dev;
        rt_device_set_rx_indicate(dev, finsh_rx_ind);
    }
static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size)
{
    RT_ASSERT(shell != RT_NULL);

    /* release semaphore to let finsh thread rx data */
    rt_sem_release(&shell->rx_sem);

    return RT_EOK;
}

接下来进入死循环的代码,主要逻辑代码部分

ch = finsh_getchar(); // ch的定义在前面,忽略了
            |
            |
            v
static char finsh_getchar(void)
{
#ifdef RT_USING_POSIX
    return getchar();
#else
    char ch;

    RT_ASSERT(shell != RT_NULL);
    while (rt_device_read(shell->device, -1, &ch, 1) != 1)
        rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);

    return ch;
#endif
}
/* handle end of line, break */
        if (ch == '\r' || ch == '\n')
        {
#ifdef FINSH_USING_HISTORY
            shell_push_history(shell);
#endif

#ifdef FINSH_USING_MSH
            if (msh_is_used() == RT_TRUE)
            {
                if (shell->echo_mode)
                    rt_kprintf("\n");
                msh_exec(shell->line, shell->line_position);
            }
            else //这个else就是在这的,下面的代码懒得贴
#endif
    int cmd_ret;

    /* strim the beginning of command */
    while (*cmd  == ' ' || *cmd == '\t')
    {
        cmd++;
        length--;
    }

    if (length == 0)
        return 0;

    /* Exec sequence:
     * 1. built-in command
     * 2. module(if enabled)
     * 3. chdir to the directry(if possible)
     */
    if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0)
    {
        return cmd_ret;
    }
/* truncate the cmd at the first space. */
    {
        char *tcmd;
        tcmd = cmd;
        while (*tcmd != ' ' && *tcmd != '\0')
        {
            tcmd++;
        }
        *tcmd = '\0';
    }
    rt_kprintf("%s: command not found.\n", cmd);
    return -1;

ok,现在回到_msh_exec_cmd函数中去,看看它里面是如何实现,直接贴上整个函数体代码

static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp)

    int argc;
    rt_size_t cmd0_size = 0;
    cmd_function_t cmd_func;
    char *argv[RT_FINSH_ARG_MAX];

    RT_ASSERT(cmd);
    RT_ASSERT(retp);

    /* find the size of first command */
    while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
        cmd0_size ++;
    if (cmd0_size == 0)
        return -RT_ERROR;

    cmd_func = msh_get_cmd(cmd, cmd0_size);
    if (cmd_func == RT_NULL)
        return -RT_ERROR;
      ...
static cmd_function_t msh_get_cmd(char *cmd, int size)
{
    struct finsh_syscall *index;
    cmd_function_t cmd_func = RT_NULL;

    for (index = _syscall_table_begin;
            index < _syscall_table_end;
            FINSH_NEXT_SYSCALL(index))
    {
        if (strncmp(index->name, "__cmd_", 6) != 0) continue;

        if (strncmp(&index->name[6], cmd, size) == 0 &&
                index->name[6 + size] == '\0')
        {
            cmd_func = (cmd_function_t)index->func;
            break;
        }
    }

    return cmd_func;
}
//前面定义了int argc和char *argv[RT_FINSH_ARG_MAX];
//RT_FINSH_ARG_MAX = 10
/* split arguments */
    memset(argv, 0x00, sizeof(argv));
    argc = msh_split(cmd, length, argv);
    if (argc == 0)
        return -RT_ERROR;

    /* exec this command */
    *retp = cmd_func(argc, argv);
    return 0;

然后又回到线程执行函数里,继续往下走...待续

#ifdef FINSH_USING_MSH
            if (msh_is_used() == RT_TRUE)
            {
                if (shell->echo_mode)
                    rt_kprintf("\n");
                msh_exec(shell->line, shell->line_position);
            }
            else
#endif  //--------------------------------------------------->上面是刚刚解析命令成功的情况,从下面开始
            {
#ifndef FINSH_USING_MSH_ONLY
                /* add ';' and run the command line */
                shell->line[shell->line_position] = ';';

                if (shell->line_position != 0) finsh_run_line(&shell->parser, shell->line);
                else
                    if (shell->echo_mode) rt_kprintf("\n");
#endif
            }

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

标签:RT,rt,shell,Thread,FINSH,cmd,线程,device
来源: https://www.cnblogs.com/Joy2013Ru/p/13733913.html