关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)
“可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。”(摘自嵌入式实时操作系统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
可重入函数模拟堆 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)