1、
“可重入函数能够被一个以上的使命调用,而不用忧虑数据被损坏。可重入函数任何时候都能够被中止,一段时间今后又能够运转,而相应的数据不会丢掉。”(摘自嵌入式实时操作体系uC/OS-II)
在了解上述概念之前,有必要先说一下keilc51的“掩盖技能”。(选用该技能的原因请看附录中一网友的解说)
(1)部分变量存储在大局RAM空间(不考虑扩展外部存储器的状况);
(2)在编译链接时,即现已完结部分变量的定位;
(3)假如各函数之间没有直接或直接的调用联系,则其部分变量空间便可掩盖。
正是因为以上的原因,在Keil C51环境下,朴实的函数假如不加处理(如添加一个模仿栈),是无法重入的。举个比如:
void TaskA(void* pd)
{
int a;
//其他一些变量界说
do{
//实践的用户使命处理代码
}while(1);
}
void TaskB(void* pd)
{
int b;
//其他一些变量界说
do{
func();
//其他实践的用户使命处理代码
}while(1);
}
void func()
{
int c;
//其他变量的界说
//函数的处理代码
}
在上面的代码中,TaskA与TaskB并不存在直接或直接的调用联系,因此它们的部分变量a与b就是能够被相互掩盖的,即它们或许都被定坐落某一个相同的RAM空间。这样,当TaskA运转一段时间,改动了a后,TaskB获得CPU操控权并运转时,便或许会改动b。因为a和b指向相同的RAM空间,导致TaskA从头获得CPU操控权时,a的值现已改动,然后导致程序运转不正确,反过来亦然。另一方面,func()与TaskB有直接的调用联系,因此其部分变量b与c不会被相互掩盖,但也不能确保func的部分变量c不会与TaskA或其他使命的部分变量构成可掩盖联系。
依据上述剖析咱们很简略就能够判别出TaskA和TaskB这两个函数是不行重入的(当然,func也不行重入)。那么怎么让函数成为可重入函数呢?C51编译器选用了一个扩展关键字reentrant作为界说函数时的选项,需要将一个函数界说为可重入函数时,只要在函数后边加上关键字reentrant即可。
存储形式 |
栈指针 |
栈区域 |
Small |
?C_IBP(1字节) |
直接拜访的内部数据存储器(IDATA),栈区最大为256字节 |
Compact |
?C_PBP(1字节) |
分页寻址的外部数据存储器(PDATA),栈区最大为256字节 |
Large |
?C_XBP(2字节) |
外部数据存储器(XDATA),栈区最大为64K |
表1
留意:51系列单片机的体系仓库(也叫硬件仓库或惯例栈)总是坐落内部数据存储器中(SP为8位寄存器,只能指向内部),并且是“向上成长”型的(从低地址向高地址),而模仿栈是“向下成长”型的。
2、
在进入剖析之前,先简略讲讲c51函数调用时参数是怎么传递的。简略来说,参数主要是经过寄存器R1~R7来传递的,假如在调用时,参数无寄存器可用或是选用了编译操控指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所挑选的存储器形式。使用51单片机的作业寄存器最多传递3个参数,如表2所示。
传递的参数 |
char、1字节指针 |
int、2字节指针 |
long、float |
一般指针 |
第一个参数 |
R7 |
R6,R7 |
R4~R7 |
R1,R2,R3 |
第二个参数 |
R5 |
R4,R5 |
R4~R7 |
R1,R2,R3 |
第三个参数 |
R3 |
R2,R3 |
无 |
R1,R2,R3 |
表二
举两个比如:
func1(int a):“a”是第一个参数,在R6,R7中传递;
func2(int b,int c,int *d):“b”在R6,R7中传递,“c”在R4,R5中传递,“*d”则在R1,R2,R3中传递。
至于函数的回来值经过哪些寄存器或是什么办法传递这儿就不说了,咱们能够看看c51的相关文档或是书本。
好了,接下来咱们开端剖析一个简略的程序,代码如下:
int fun(char a, char b, char c, char d ) reentrant
{
}
main()
{
}
程序很简略,废话少说,下面跟我一同看看c51翻译成的汇编语言是什么姿态的。
main()
{
int i;
i = fun(1,2,3,4);
MOV
LCALL
;指向0xFFFF
MOV
MOVX
MOV
MOV
MOV
LCALL
MOV
;并存储在外部数据存储器0x0000和0x0001处
;(int型为两个字节)
MOV
MOVX
INC
MOV
MOVX
}
RET
阐明:模仿栈指针开始在startup.a51中初始化为0xFFFF+1;由以上汇编代码能够看出参数是从右往左扫描的。
接下来看看fun的汇编代码:(很长,咱们耐性看吧,有些能够越过的)
C:0003
MOV
LCALL
MOV
MOVX
MOV
LCALL
MOV
MOVX
MOV
LCALL
MOV
MOVX
MOV
LCALL
;部分int变量做准备
j1 = a + b + c +d;
MOV
LCALL
;一个参数,此刻DPTR=0xFFFF
;留意:C?XBPOFF不改动C?XBP的值
MOVX
MOV
MOV
。。。。。。
。。。。。。;省掉,完结取参数2,取参数3,取参数4并相加
。。。。。。