finSH介绍
FinSH 是 RT-Thread 的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。它可以使用串口 / 以太网 / USB 等与 PC 机进行通信。
命令执行过程
功能:
- 支持鉴权,可在系统配置中选择打开/关闭。(TODO:具体操作过程)
- tap键自动补全命令,如果什么都没有输入会列出所有命令。(TODO:原理)
按键 功能描述 Tab 键 当没有输入任何字符时按下 Tab 键将会打印当前系统支持的所有命令。若已经输入部分字符时按下 Tab 键,将会查找匹配的命令,也会按照文件系统的当前目录下的文件名进行补全,并可以继续输入,多次补全 ↑↓键 上下翻阅最近输入的历史命令 退格键 删除符 ←→键 向左或向右移动标 - 历史命令查看。(上下键翻阅)
- FinSH 完全采用 ANSI C 编写,具备极好的移植性。(前面章节中介绍的函数方式动态地向 FinSH 添加符号,FinSH 将不会动态申请内存??什么方式?)
传统命令行模式msh VS C-style模式
最初 FinSH 仅支持 C-Style 模式,后来随着 RT-Thread 的不断发展,C-Style 模式在运行脚本或者程序时不太方便,而使用传统的 shell 方式则比较方便。另外,C-Style 模式下,FinSH 占用体积比较大。(TODO:为什么会比较大)出于这些考虑,在 RT-Thread 中增加了 msh 模式,msh 模式体积小,使用方便,推荐使用 msh 模式。
-
mode msh C-style example command [arg1] [arg2] […] list_thread() switch exit ===>to c-style mode msh() ===> to msh mode cmd define MSH_CMD_EXPORT(name, desc);
MSH_CMD_EXPORT_ALIASFINSH_FUNCTION_EXPORT(name,desc);
FINSH_VAR_EXPORT(name, type, desc);
FINSH_FUNCTION_EXPORT_ALIAS(name, alias, desc);(当函数名称超过16个字节的时候,需要导出别名,否则执行会出错,在重命名的命令名字前加__cmd_
就可以将命令导出到 msh 模式,否则,命令会被导出到 C-Style 模式)
FinSH功能配置
在rt-thread配置文件rt-config.h 中,在env开发环境下,可以使用menuconfig 命令在图形化界面当中进行配置。
宏定义 | 取值类型 | 描述 | 默认值 |
---|---|---|---|
#define RT_USING_FINSH | 无 | 使能 FinSH | 开启 |
#define FINSH_THREAD_NAME | 字符串 | FinSH 线程的名字 | “tshell” |
#define FINSH_USING_HISTORY | 无 | 打开历史回溯功能 | 开启 |
#define FINSH_HISTORY_LINES | 整数型 | 能回溯的历史命令行数 | 5 |
#define FINSH_USING_SYMTAB | 无 | 可以在 FinSH 中使用符号表 | 开启 |
#define FINSH_USING_DESCRIPTION | 无 | 给每个 FinSH 的符号添加一段描述 | 开启 |
#define FINSH_USING_MSH | 无 | 使能 msh 模式 | 开启 |
#define FINSH_USING_MSH_ONLY | 无 | 只使用 msh 模式 | 开启 |
#define FINSH_ARG_MAX | 整数型 | 最大输入参数数量 | 10 |
#define FINSH_USING_AUTH | 无 | 使能权限验证 | 关闭 |
#define FINSH_DEFAULT_PASSWORD | 字符串 | 权限验证密码 | 关闭 |
FiniSH代码阅读提示:
每次的命令执行都是在 FinSH 线程(即 tshell 线程)的上下文中完成。
初始化:finsh_system_init()
函数。
输出:在 RT-Thread 中依赖 rt_kprintf()
输出。在启动函数 rt_hw_board_init()
中,rt_console_set_device(const char* name)
函数设置了 FinSH 的打印输出设备。
输入:获得了信号量后,调用
rt_device_read()
函数从设备 (选用串口设备) 中获得一个字符然后处理。而 rx_sem 信号量的释放通过调用rx_indicate()
函数以完成对 FinSH 线程的输入通知。通常的过程是,当串口接收中断发生时(即串口有输入),接受中断服务例程调用rx_indicate()
函数通知 FinSH 线程有输入,而后 FinSH 线程获取串口输入最后做相应的命令处理。
TODO:获得一个字符就处理吗?还是整个命令获得了再处理?
FinSH代码阅读
文件结构
route:rt-thread/components/finish
├─ finsh
│ ├─ cmd.c // place rt-thread cmd
│ ├─ finsh.h // cmd export相关的宏和函数的声明,涉及到的函数的声明(TODO:函数实现在哪?)
│ ├─ msh.c // msh cmd search & excution funcitons
│ ├─ msh.h // msh cmd search & excution funcitons API
│ ├─ msh_file.c // some os cmd define(cd/pwd/mkfs/mount/...)
│ ├─ msh_parse.c // msh cmd parse
│ ├─ msh_parse.h // msh cmd parse API
│ ├─ shell.c // shell context and cmd read
│ ├─ shell.h // struct define and API set
│ ├─ Kconfig // project config file for menuconfig
│ └─ SConscript // scons project file
初始化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1M5zlpzZ-1667636697535)(a1992662197db6b981a36dd150b62ae0.png)]
finSH的初始化放在rt_components_init()的applilication init functions部分。
调用关系:
rtthread_startup() -->
rt_system_scheduler_start() -->
main_thread_entry(void *parameter) -->
rt_components_init(void) -->
finsh_system_init(void) -->
分出另一个线程 -->
finsh_thread_entry(void *parameter).
finsh_system_init() 下finsh_system_function_init() 的作用是什么呢?
答:初始化两个指针,一个指向所有命令的最开始,一个指向命令的最结尾。
CMD define逻辑
macro call :FINSH_FUNCTION_EXPORT(name, desc) --> MSH_FUNCTION_EXPORT_CMD(name, cmd, desc)
MSH_FUNCTION_EXPORT_CMD逻辑
#define MSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \
const char __fsym_##cmd##_name[] = #cmd; \ /* cmd name to str */
const char __fsym_##cmd##_desc[] = #desc; \ /* cmd description to str */
__declspec(allocate("FSymTab$f")) \ /* put next struct to this section */
const struct finsh_syscall __fsym_##cmd = \
{ \
__fsym_##cmd##_name, \
__fsym_##cmd##_desc, \
(syscall_func)&name \
};
FinSH CMD 处理逻辑
finsh_thread_entry函数用于处理来自用户的输入,大概有以下几个功能:
- 鉴权
- 循环读取一个字符串并处理:
- 特殊字符的处理: 上下左右、制表符、回退、回车。对应历史命令读取、光标移动、命令和路径补全、删除当前命令、执行命令功能。
- 普通字符,放入字符串存储等待处理。
流程图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PsFWhmpP-1667636697536)(6fc6a5a21447eeb3ce2ceeedebc41719.png)]
FinSH shell 状态机
初步阅读代码,我的理解是这个是为了处理上下左右四个键的输入方式而设计的。
WAIT_NORMAL // shell 处于等待接收正常字符的状态
WAIT_SPEC_KEY // shell 处于等待接收方向键的特殊字符的状态
WAIT_FUNC_KEY // shell 处于等待接收功能键的状态
相关的特殊按键的获取方式:
ps:在Win的env工具下仿真模拟msh读取不到上下左右的按键,猜测和windows 和 linux 下的按键的输入值不同有关。
Key | Linux | Key | Windows |
---|---|---|---|
Up | 0x1B 0x5B 0x41 | Up | 0xE0 0x48 |
Down | 0x1B 0x5B 0x42 | Down | 0xE0 0x50 |
Left | 0x1B 0x5B 0x44 | Left | 0xE0 0x4B |
Right | 0x1B 0x5B 0x43 | Right | 0xE0 0x4D |
FinSH context
struct finsh_shell
{
struct rt_semaphore rx_sem;
enum input_stat stat;
rt_uint8_t echo_mode: 1;
rt_uint8_t prompt_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
char line[FINSH_CMD_SIZE + 1];
rt_uint16_t line_position;
rt_uint16_t line_curpos;
#if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE)
rt_device_t device;
#endif
#ifdef FINSH_USING_AUTH
char password[FINSH_PASSWORD_MAX];
#endif
};
FinSH input信息读取
在rt_hw_board_init()
中调用rt_device_t rt_console_set_device(const char *name)
注册了console device。