Linux和RTOS shell

1 Linux shell
1.1 Linux console
@ init/main.c
static int __ref kernel_init(void *unused)
{
    kernel_init_freeable();
    […]
    if (ramdisk_execute_command) {
        if (!run_init_process(ramdisk_execute_command))
            return 0;
        pr_err("Failed to execute %s\n", ramdisk_execute_command);
    }
    […]
}

static noinline void __init kernel_init_freeable(void)
{  
    […]
    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  
        pr_err("Warning: unable to open an initial console./n");  
    (void) sys_dup(0);  
    (void) sys_dup(0);  
    […]
    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }
    […]
}  

从上面的代码中可以看到,它先open了/dev/console,在open的时候,会去找进程没使用的最小文件序号,而当前进程没有打开任何文件,所以sys_open()的时候肯定会找到0,然后两次调用sys_dup(0)来复制文件描述符0,复制后的文件描述符肯定是1、2,这样0、1、2 就建立起来了。
然后kernel_init调用run_init_process()->do_execve()将当前进程替换成了用户空间的一个进程,这也就是用户空间init进程的由来;此后用户空间的进程全是它的子孙进程,也就共享了0、1、2的文件描述符了,这也就是我们所说的stdin、stdout和stderr。

uart_driver.state包括uart_driver.nr个slot,每个slot就对应一个uart_port。

/dev/console - 设备号(5,1)
/dev/ptmx:Multiplex
/dev/tty - 设备号(5,0),进程的控制终端(/dev/tty)存放在task_struct.signal->tty(函数__proc_set_tty)中;使用tty或者busybox tty查看(对应函数ttyname(int))
/dev/tty0 - 设备号(4,0),虚拟终端,主要指显示屏,Ubuntu上可以使用ps -ax查看

1.2 获取进程tty name
#include <stdio.h>
#include <stdlib.h>
#include "fcntl.h"
#include "unistd.h"

static void redirect_std_ioe(void)
{
    int fd = open("/dev/kmsg", O_WRONLY);

    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    close(fd);
}

int main(int argc, char **argv)
{
    printf("stdin: %s\n", ttyname(0));
    printf("stdout: %s\n", ttyname(1));
    printf("stderr: %s\n", ttyname(2));

    // demo
    redirect_std_ioe();
    return 0;
}
需要注意的是子进程的stdin, stdout和stderr继承于父进程。

1.3 Arrow Keys
每个方向键包含3个键值,如下所示,在写shell解析程序时需要注意,只有捕获到3个键值时才认为方向键按下了。
\033[2J - 清屏(0x1b, 0x5b, ‘2’, ‘J’)
Format:ESC[2J - 清除显示,清除屏幕并将光标位置移到起始位置(0 行,0 列)。
\033[2J\033[0;0H - 清屏且将光标置顶(0x1b, 0x5b, ‘0’, ‘;’, ‘0’, ‘H’)
Format:ESC[X;YH - 光标位置,移动光标到指定的位置(坐标),如果不指定位置,光标将移动到起始位置,即屏幕的左上角(0 行,0 列)。

// up - "\033[A"
// down - "\033[B"
// left - "\033[D"
// right - "\033[C"
// Arrow keys contain three characters
// they are 0x1b, 0x5b, <A|B|D|C>
// Putty <Terminal->Keyboard->The Function keys and keypad>
// choose Linux

Refer to “linux 读取键盘上下左右键小程序”
http://blog.csdn.net/cean1024/article/details/72903069

1.4 Android mksh
修改mksh默认的列长度
@ external/mksh/src/sh.h
EXTERN mksh_ari_t x_cols E_INIT(80);    /* tty columns */
EXTERN mksh_ari_t x_lins E_INIT(24);    /* tty lines */

1.5 Android指定service的console
lsof -p <PID>

@ init.xxx.rc
service xxx /system/bin/xxx
    class main
    console // 将stdio定向到/dev/console,否则到/dev/null

1.6 adb shell
注意的是adb shell使用的是pty,即伪终端(ptmx和pts)。
@ adb/services.c
static int create_subproc_pty(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
{
    [...]
    if (*pid == 0) {
        init_subproc_child();

        int pts = unix_open(devname,
            O_RDWR | O_CLOEXEC);
        if (pts < 0) {
            fprintf(stderr,
                "child failed to open pseudo-term slave: %s\n",
                devname);
            exit(-1);
        }
        // 重定向shell进程的stdin, stdout和stderr到伪终端pts,使用busybox tty查看shell的控制终端
        // shell进程从pts读数据
        dup2(pts, STDIN_FILENO); 
        // shell进程向pts写数据
        dup2(pts, STDOUT_FILENO);
        // shell进程向pts写数据
        dup2(pts, STDERR_FILENO);

        adb_close(pts);
        adb_close(ptm);

        execl(cmd, cmd, arg0, arg1, NULL);
        fprintf(stderr,
            "- exec '%s' failed: %s (%d) -\n",
            cmd, strerror(errno), errno);
        exit(-1);
    } else {
        return ptm; // adb从USB ep读数据,再向ptm写数据;或者从ptm读数据,再写到USB ep
    }
}

1.7 拓展
自定义shell终端提示符
https://www.cnblogs.com/lienhua34/p/5018119.html

ANSI的Esc屏幕控制:
http://blog.csdn.net/lzuacm/article/details/8993785

\033[2J - 清屏(0x1b, 0x5b, ‘2’, ‘J’)
Format:ESC[2J - 清除显示,清除屏幕并将光标位置移到起始位置(0 行,0 列)。
\033[2J\033[0;0H - 清屏且将光标置顶(0x1b, 0x5b, ‘0’, ‘;’, ‘0’, ‘H’)
Format:ESC[X;YH - 光标位置,移动光标到指定的位置(坐标),如果不指定位置,光标将移动到起始位置,即屏幕的左上角(0 行,0 列)。

console参考代码
https://wenku.baidu.com/view/5387f1d94028915f804dc2dc.html

2 RTOS
2.1 eCos
/*
 * minishell.c
 *
 * Copyright (C) 2017
 *
 * Author: George Tso <zoosenpin@163.com>
 *
 */
#include <oem-portmisc.h>

#define PROMT "shell>"
#define SHELL_MAX_CMD_SZ 512
#define SHELL_MAX_ARGV     25

static char *argv_array[SHELL_MAX_ARGV];
static char cur_cmd_buf[SHELL_MAX_CMD_SZ];
static cyg_mutex_t shell_mutex;
static int ser_fd;

typedef enum {
    TYPE_SHELL_PBUS = 1,
    TYPE_SHELL_SYSTEM = 2,
    TYPE_SHELL_WIRELESS = 3,
} enum_type_shell_cmd_t;

typedef struct shell_cmd {
    char *name;
    int    (*func)(struct shell_cmd *, int, char **);
    char *help;
    int mode; // future use
    struct shell_cmd *prev;
    struct shell_cmd *next;
} shell_cmd_t;

static shell_cmd_t header_list;

static void print_help(shell_cmd_t *cmd_list)
{
    char buf[SZ_256];
    shell_cmd_t *cp;

    memset(buf, 0, SZ_256);
    cyg_mutex_lock(&shell_mutex);
    for (cp = cmd_list->next;
        cp;
        cp = cp->next) {
        strcat(buf, cp->name);
        strcat(buf, " ");
    }
    cyg_mutex_unlock(&shell_mutex);
    ser_printf("%s\n", buf);
}

static int get_argc(const char *string)
{
    int argc = 0;
    char *p = (char *)string;

    while (*p) {
        if (*p != ' ' && *p) {
            argc++ ;
            while (*p != ' ' && *p) p++;
            continue;
        }
        p++ ;
    }
    if (argc >= SHELL_MAX_ARGV)
        argc = SHELL_MAX_ARGV - 1;
    return argc;
}

static char** get_argv(const char* string)
{
    char *p;
    int n = 0;

    memset(argv_array,
        0,
        SHELL_MAX_ARGV * sizeof(char *));
    p = (char* )string;
    while (*p) {
        argv_array[n] = p ;
        while(*p != ' ' && *p) p++;
        *p++ = '\0';
        while(*p == ' ' && *p) p++;
        n++ ;
        if (n == SHELL_MAX_ARGV)
            break;
    }
    return (char** )&argv_array;
}

static void get_cmd_from_ser(char *buf,
    char *promt)
{
    fd_set rd_set;
    int clen, size=0;
    int err;
    int readlen;
    struct timeval timeout;
    unsigned char c;

    while (1) {
        if (size >= (SHELL_MAX_CMD_SZ - 1)) {
            buf[0] = '\0';
            return;
        }

        FD_ZERO(&rd_set);
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        FD_SET(ser_fd, &rd_set);

        c = 0;
        err = select(ser_fd + 1,
            &rd_set,
            NULL,
            NULL,
            &timeout);
        if (err == 0) // select timeout
            continue;

        if (err < 0) {
            ser_printf("select returned error: %s\n",
                strerror(err));
            break;
        }

        if (FD_ISSET(ser_fd, &rd_set)) {
            readlen = read(ser_fd, &c, 1);
            if (readlen < 1)
                continue;
        }

        switch (c) {
            case 0x8:
            case 0x7f:
                // erase one character + 'backspace' char
                size -= 1;
                if (size < 0) {
                    size = 0;
                } else {
                    clen = 3;
                    ser_printf("\b \b");
                }
                continue;
            case 0x3b:
            case 0xd:
                if (FD_ISSET(ser_fd, &rd_set)) {
                    clen = 1;
                    ser_printf("\n");
                } else {
                    clen = 1;
                    //write(ser_fd, "\r\n", 2);
                    ser_printf("\n");
                }
                buf[size] = '\0';
                // got a command
                return;
            case 0xa:
                if (FD_ISSET(ser_fd, &rd_set)) {
                    clen = 1;
                    ser_printf("\r");
                }
                continue;
        }

        if (FD_ISSET(ser_fd, &rd_set)) {
            clen = 1;
            ser_printf("%c", c);
        }
        buf[size++] = c;
    }
}

static void cmd_parser_thread(
    cyg_addrword_t data)
{
    char **argv;
    int argc;
    int count;
    u32 enter_lvl = 0;
    shell_cmd_t *cp;
    shell_cmd_t *cur_cmd_list =
        (shell_cmd_t *)data;

    if (!cur_cmd_list) {
        return;
    }

    while (1) {
        ser_printf("%s", PROMT);
        get_cmd_from_ser(cur_cmd_buf,
            PROMT);

        argc = get_argc((const char *)cur_cmd_buf);
        argv = (char **)get_argv(
            (const char *)cur_cmd_buf);
        if(argc < 1) continue;

        if ((!strcmp(argv[0], "help") ||
            (*(argv[0]) == '?'))) {
            print_help(cur_cmd_list);
        } else if (!strcmp(argv[0], "ls" )) {
            print_help(cur_cmd_list);
        } else if (!strcmp(argv[0], "cd")) {
            // TODO
            if (argc > 1 &&
                !strcmp(argv[1], "..")) {
                if (0 == enter_lvl) {
                    continue;
                } else if (enter_lvl > 0) {
                    enter_lvl--;
                }
            } else if (argc > 1) {
                enter_lvl++;
                //strcpy(argv[0], argv[1]);
            }
        } else {
            cyg_mutex_lock(&shell_mutex);
            for (cp = cur_cmd_list->next,count = 0;
                cp;
                cp = cp->next) {
                if (!strcasecmp(argv[0],
                    cp->name) &&
                    cp->func) {
                    cp->func(cp, argc, argv);
                    break;
                }
            }
            cyg_mutex_unlock(&shell_mutex);
        }
    }
}

int __api oem_register_shell_cmd(
    enum_type_shell_cmd_t type,
    shell_cmd_t *new_c)
{
    shell_cmd_t *cur_c = &header_list;

    while (cur_c) {
        if (cur_c == new_c) {
            return -1;
        }
        cur_c = cur_c->next;
    }

    cyg_mutex_lock(&shell_mutex);
    cur_c = &header_list;
    while (cur_c && cur_c->next)
        cur_c = cur_c->next;

    cur_c->next = new_c;
    new_c->next = NULL;
    new_c->prev = cur_c;
    cyg_mutex_unlock(&shell_mutex);

    return 0;
}

int __api oem_unregister_shell_cmd(
    enum_type_shell_cmd_t type,
    shell_cmd_t *new_c)
{
    bool found = false;
    shell_cmd_t *cur_c = &header_list;

    while (cur_c) {
        if (cur_c == new_c) {
            found = true;
            break;
        }
        cur_c = cur_c->next;
    }
    if (!found) {
        return -1;
    }

    cyg_mutex_lock(&shell_mutex);
    if (!new_c->next) {
        new_c->prev->next = NULL;
        new_c->prev = NULL;
    } else {
        new_c->next->prev = new_c->prev;
        new_c->prev->next = new_c->next;
        new_c->next = NULL;
        new_c->prev = NULL;
    }
    cyg_mutex_unlock(&shell_mutex);
    return 0;
}

// DEMO
static int nvm_op(shell_cmd_t *cmd,
    int argc, char **argv)
{
    int32 ret;
    u32 d32_val1, d32_val2;

    if (argc < 2) {
        ser_printf("%s", cmd->help);
        return -1;
    }

    if (2 == argc) {
        d32_val1 = strtoul(argv[1], NULL, 0);
        ret = nv_read(d32_val1, &d32_val2, 4);
        ser_printf("%d\n", d32_val2);
    } else if (3 == argc) {
        d32_val1 = strtoul(argv[1], NULL, 0);
        d32_val2 = strtoul(argv[2], NULL, 0);
        ret = nv_write(d32_val1, &d32_val2, 4);
        ret = nv_commit();
        if (0 == ret) {
            ser_printf("succeeded in writing %d to nv%d\n",
                d32_val2, d32_val1);
        } else {
            ser_printf("failed to write %d to nv%d\n",
                d32_val2, d32_val1);
        }
    } else {
        return -1;
    }
    return ret;
}

shell_cmd_t nvm_cmd = {
    "nvm", nvm_op,
    "usage:\nnvm [id]\nnvm [id] [val]\n", 0, 0
};

static int shell_init(void)
{
    ser_fd = open("/dev/ser0", O_RDWR);
    if (ser_fd < 0) {
        ser_printf(
            "open(/dev/ser0) returned error: %s\n",
            strerror(ser_fd));
        return -1;
    }
    cyg_mutex_init(&shell_mutex);
    memset(&header_list, 0, sizeof(header_list));
    oem_register_shell_cmd(
        TYPE_SHELL_SYSTEM,
        &nvm_cmd);
    return 0;
}

static char stack[SHELL_THREAD_STACK_SZ];
static cyg_thread thread_data;
static cyg_handle_t thread_handle;

void shell_thread_init(void)
{
    if (0 == shell_init()) {
        cyg_thread_create(
                SHELL_THREAD_PRIORITY,
                cmd_parser_thread,
                (cyg_addrword_t)&header_list,
                "cmd_parser_thread",
                &stack[0],
                SHELL_THREAD_STACK_SZ,
                &thread_handle,
                &thread_data
                );
        cyg_thread_resume(thread_handle);
    }
}

2.2 FreeRTOS
@flash.ld
SECTIONS
{
    [...]
    .ram_text :
    {
        [...]

        . = ALIGN(4);
         PROVIDE (__gcc_section_cmds = .);
         KEEP(*(SORT(.oem_rodata.cmds*)))
         PROVIDE (__gcc_section_cmds_end = .);

        [...]
    }
    [...]
}

@console.h
typedef struct cli_node {
    const char *name;
    int16_t type;
    int (*func)(struct cli_node *, int, char **);
    const char *help;
    /* void *prvdata; */
    struct cli_node *prev;
    struct cli_node *next;

    /* directory node */
    struct cli_node *de_parent;
    struct cli_node *de_child;

    /* foreground proc handler */
    struct list_head fg_list

    int (*kill_cb)(struct cli_node *);
} cli_node_t;

extern const cli_node_t __gcc_section_cmds[];
extern const cli_node_t __gcc_section_cmds_end[];

#if 1
#define DECLARE_CONSOLE_COMMAND(NAME, ROUTINE, HELP) \
static const char __con_node_label_##NAME[] = #NAME;\
static const cli_node_t __con_node_##NAME                   \
__attribute__((used,section(".oem_rodata.cmds." #NAME))) =\
{                                                     \
    .name = __con_node_label_##NAME, \
    .type = 0,                                    \
    .func = ROUTINE,                      \
    .help = HELP,                              \
}

#define DECLARE_CONSOLE_COMMAND_WKILL(NAME, ROUTINE, HELP, KILL_CB) \
static const char __con_node_label_##NAME[] = #NAME; \
static const cli_node_t __con_node_##NAME   \
__attribute__((used,section(".oem_rodata.cmds." #NAME))) = \
{                                                     \
    .name = __con_node_label_##NAME, \
    .type = 0,                                   \
    .func = ROUTINE,                     \
    .help = HELP,                            \
    .prev = NULL,                            \
    .next = NULL,                            \
    .de_parent = NULL,                  \
    .de_child = NULL,                     \
    .fg_list = {                                  \
        .next = NULL,                        \
        .prev = NULL,                       \
    },                                                \
    .kill_cb = KILL_CB,                    \
}
#else
#define DECLARE_CONSOLE_COMMAND(NAME, ROUTINE, HELP)   \
    int (ROUTINE)(struct cli_node *cmd, int argc, char **argv) __attribute__((unused))
#define DECLARE_CONSOLE_COMMAND_WKILL(NAME, ROUTINE, HELP, KILL_CB)\
    int (ROUTINE)(struct cli_node *cmd, int argc, char **argv) __attribute__((unused)); \
    int (KILL_CB)(struct cli_node *cmd) __attribute__((unused))
#endif

@ console.c
static bool gcc_section_cmd(int argc,
    char **argv, cli_node_t **cli_head)
{
    const cli_node_t *cli_node;
    uint8_t match_length = strlen(argv[0]);

    for (cli_node = __gcc_section_cmds;
        cli_node < __gcc_section_cmds_end;
        cli_node++) {
        if (!strncasecmp(argv[0],
            cli_node->name,
            match_length)) {
            if (cli_node->func &&
                (CLI_PROC_ACTION_FG == cli_node->func(cli_node, argc, argv))) {
                fg_proc_list_add(cli_node);
            }
            return true;
        }
    }
    return false;
}

static void list_gcc_section_cmds(void)
{
    const cli_node_t *cli_node;
    char buf[SZ_256], *cp = buf;
    int16_t cp_sz = SZ_256, ret;

    ret = snprintf(cp, cp_sz,
        "OEM_RODATA.CMDS:"CRLF);
    if (ret > 0) {
        cp += ret;
        cp_sz -= ret;
    }

    for (cli_node = __gcc_section_cmds;
        cli_node < __gcc_section_cmds_end;
        cli_node++) {
        ret = snprintf(cp, cp_sz,
            "x    %s"CRLF, cli_node->name);
        if (ret > 0) {
            cp += ret;
            cp_sz -= ret;
        }
    }
    console_puts("%s", buf);
}

3 Abbreviations
lex:lexical,词法分析
PS: Prompt Statement

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值