热爱开源,喜欢分析操作系统架构
分类: C/C++
2012-11-03 22:09:27
于对程序员来说,除了机器码之外,不管你用什么语言编写程序都离不开编译器。但是我们所写下的程序,都是以我们能够看的懂的文本形式保存的,虽然人能看得懂但不代表机器也能够看的懂。所以在文本程序到编译到可执行文件之前最先要完成的步骤就是语法的解释。比如下段简单的代码
编译器从开始读一直读取到‘;’,获取整个语法段。按照c语言的优先级,这个语句先被分为3个部分,{int a} {=}和{2+3*(2-1)}。对于int a是需要申请一个typeof(int)大小的内存,这个内存标记为符合’a’,之后这个namespace空间里所有出现'a'的地方都会被指定到这块内存。‘=’是一个赋值符合,这个符合会联合之前和之后的两个语法段,也就是将{2+3*(2-)}的结果赋给‘a’所指的内存。具体针对于{2+3*(2-1)},语法解释器会先按照优先级的顺序,先括号、乘法再是加法,继而获得这个部分的值。总的来说是以下这个一个步骤:
点击(此处)折叠或打开
如果说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添加到系统中的。
点击(此处)折叠或打开
对于上述的hello函数,通过调用FINSH_FUNCTION_EXPORT就可以添加到finish shell中,在终端中输入hello(),串口就可以打印"Hello RT-Thread!"。
点击(此处)折叠或打开
具体分析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文件我们可以看到
点击(此处)折叠或打开
点击(此处)折叠或打开
- 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);
点击(此处)折叠或打开
再看一下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函数。
点击(此处)折叠或打开
注意这里将函数添加到了global_syscall_list链表中了,而这个链表的功能是相当于syscall_table的,只不过它是负责动态生成的SYS_CALL。与之对应的有global_sysvar_list链表,其对应的是SYS_VAR。