RT-Thread交互接口finish框架分析

RT-Thread的经典的shell框架分析,在使用单片机或其他MCU开发的时候交互式必不可少的,一般都采用串口交互的方式。

linux 的shell是属于脚本类的语言风格,finsih shell的语言风格明显是c的。毕竟c比较偏底层,资源占用少,这个对于资源贫瘠的嵌入式系统而言是非常适合的。finish shell从usart获取文本信息,在根据一定的语法规矩将文本语言重新按执行顺序组织一遍,最后再将重新组织的语句翻译成汇编指令,最后交由cpu执行指令。当然finsih shell的汇编指令都是伪汇编指令,而且执行的环境也是虚拟出来的,而这个是编译执行过程,我之后再提。

    语句的执行是以数据为基础的。受限于系统的架构,finish shell里面的数据类型并不多,整体来说分成以下几个部分

数据类型

    针对上述的代码,在int a中,int是指代变量类型,a是变量名,因为这条指令是在串口中读取的,所以a是动态申请的变量,隶属于VAR中。像‘1’‘2’‘3’是属于int型常量,而“+”“*”“-”是符号。而SYS_VAR和SYS_CALL是在编译的时候生成的,分别通过宏FINSH_VAR_EXPORT和FINSH_FUNCTION_EXPORT添加到系统中的。

long hello()
{
   rt_kprintf("Hello RT-Thread!\n");
   return 0;
}
FINSH_FUNCTION_EXPORT(hello, say hello world);

    对于上述的hello函数,通过调用FINSH_FUNCTION_EXPORT就可以添加到finish shell中,在终端中输入hello(),串口就可以打印"Hello RT-Thread!"。

#define FINSH_FUNCTION_EXPORT(name, desc)                     \
        const char __fsym_##name##_name[] = #name;                     \
        const char __fsym_##name##_desc[] = #desc;                     \
        const struct finsh_syscall __fsym_##name SECTION("FSymTab")= \
        {                            \
            __fsym_##name##_name,    \
            __fsym_##name##_desc,    \
            (syscall_func)&name        \
        };

    具体分析FINSH_FUNCTION_EXPORT(hello, say hello world),其实这个宏就是申请了3个变量_fsym_hello_name[]=’hello’,_fsm_hello_desc[]=’Hello RT-Thread!\n’,

   _fsym_hello={_fsym_hello_name,,_fsm_hello_desc,hello}。说穿了就是_fsym_hello里有3个4字节的指针,分别指向_fsym_hello_name,_fsm_hello_desc[]和hello()函数。那_fsym_hello是如何和SYS_CALL扯上关系的呢?这就要从SECTION("FSymTab")说起了。SECTION()是一个宏变量,针对不同的编译平台对于不同的操作,但无论编译平台如何其作用是相同的。本人编译平台是MDK,打开rtthread-stm32.sct文件我们可以看到

LR_IROM1 0x08000000 0x00080000 { ; load region size_region
  ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00010000 { ; RW data
   .ANY (+RW +ZI)
  }
}

     这个文件作用主要是指出了编译工程后链接文件的各个段的地址空间。我们知道编译程序一般会生成text,rodata,data,bss以及一些其他的段,我们可以通过sct将某个函数的代码段放到某个地址空间,也可以将它的数据段放到另一个地址空间。一般来说,我们不会蛋疼的去安排每个.c文件或是函数的链接地址空间,因为系统已经默认优化好了但是我们必须知道是可以通过修改sct来改变链接地址空间的。这个相信研究过linux内核编译的同学一定心领神会。然后再看rtthread-stm32.map文件,我截取了与FSymTab相关的一段。

FSymTab$$Base       0x08013614   Number   0 led.o(FSymTab)
 __fsym_led         0x08013614   Data    12 led.o(FSymTab)
 __fsym_list_mem    0x08013620   Data    12 mem.o(FSymTab)
 __fsym_hello       0x0801362c   Data    12 cmd.o(FSymTab)
 __fsym_version     0x08013638   Data    12 cmd.o(FSymTab)
 __fsym_list_sem    0x08013644   Data    12 cmd.o(FSymTab)
 __fsym_list_event  0x08013650   Data    12 cmd.o(FSymTab)
 __fsym_list_mutex  0x0801365c   Data    12 cmd.o(FSymTab)
 __fsym_list_mailbox  0x08013668 Data    12 cmd.o(FSymTab)
 __fsym_list_msgqueue 0x08013674 Data    12 cmd.o(FSymTab)
 __fsym_list_mempool  0x08013680 Data    12 cmd.o(FSymTab)
 __fsym_list_timer  0x0801368c   Data    12 cmd.o(FSymTab)
 __fsym_list_device 0x08013698   Data    12 cmd.o(FSymTab)
 __fsym_list        0x080136a4   Data    12 cmd.o(FSymTab)
 __fsym_ls          0x080136b0   Data    12 dfs_raw.o(FSymTab)
 __fsym_mkdir       0x080136bc   Data    12 dfs_raw.o(FSymTab)
 __fsym_rm          0x080136c8   Data    12 dfs_raw.o(FSymTab)
 __fsym_cat         0x080136d4   Data    12 dfs_raw.o(FSymTab)
 __fsym_mkfs        0x080136e0   Data    12 dfs_elm.o(FSymTab)
FSymTab$$Limit      0x080136ec   Number   0 dfs_elm.o(FSymTab)
Region$$Table$$Base 0x080136ec   Number   0 anon$$obj.o(Region$$Table)
Region$$Table$$Limit 0x0801370c  Number   0 anon$$obj.o(Region$$Table)
VSymTab$$Base        0x0801370c  Number   0 cmd.o(VSymTab)
  __vsym_dummy      0x0801370c   Data    16 cmd.o(VSymTab)
VSymTab$$Limit      0x0801371c   Number   0 cmd.o(VSymTab)

   相信大家找到了_fsym_hello了吧,所谓的SECTION("FSymTab")也就是把_fsym_hello这个12个字节的常量,保存在FsymTab这个段内,链接的时候根据sct文件安排按顺序将FSymTab放到Flash的地址空间里去。也就是说,无论我在这个或那个文件用调用了FINSH_FUNCTION_EXPORT,说生成的_fsym***一定是连续分布在flash的地址空间了。FINSH_VAR_EXPORT的执行原理和FINSH_FUNCTION_EXPORT一样,只不过存的是变量的地址,我就不再重复说明了

  至于为什么要这么大费周章的将FSymTab和VSymTab的变量放到一起,就是为了方便生成syscall_table和sysvar_table。在finsh_system_init中有两个函数

finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);

finsh_system_var_init(&VSymTab$$Base, &VSymTab$$Limit);

void finsh_system_function_init(void* begin, void* end)
{
    _syscall_table_begin = (struct finsh_syscall*) begin;
    _syscall_table_end = (struct finsh_syscall*) end;
}

void finsh_system_var_init(void* begin, void* end)
{
    _sysvar_table_begin = (struct finsh_sysvar*) begin;
    _sysvar_table_end = (struct finsh_sysvar*) end;
}

    再看一下rtthread-stm32.map,是不是找到了FSymTab$$Base,FSymTab$$Limit和VSymTab$$Base, VSymTab$$Limit,就这样我们生成了syscall_table和sysvar_table,而这系统就是用这两个表来查找SYS_CALL和SYS_VAR变量的。

    除了静态的生成SYS_CALL函数外,rt_thread也支持动态的加载SYS_CALL函数。

void finsh_syscall_append(const char* name, syscall_func func)
{
    /* create the syscall */
    struct finsh_syscall_item* item;

    item = (struct finsh_syscall_item*)rt_malloc(sizeof(struct finsh_syscall_item));
    if (item != RT_NULL)
    {
        item->next = NULL;
        item->syscall.name = strdup(name);
        item->syscall.func = func;

        if (global_syscall_list == NULL)
        {
            global_syscall_list = item;
        }
        else
        {
            item->next = global_syscall_list;
            global_syscall_list = item;
        }
    }
}

     注意这里将函数添加到了global_syscall_list链表中了,而这个链表的功能是相当于syscall_table的,只不过它是负责动态生成的SYS_CALL。与之对应的有global_sysvar_list链表,其对应的是SYS_VAR。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值