您的位置 首页 IC

关于可重入函数(可再入函数)和模仿仓库(仿真仓库)

1、关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候

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即可。

与非可重入函数的参数传递和部分变量的存储分配办法不同,C51编译器为可重入函数生成一个模仿栈(相对于体系仓库或是硬件仓库来说),经过这个模仿栈来完结参数传递和寄存部分变量。模仿栈以大局变量?C_IBP、?C_PBP和?C_XBP作为栈指针(体系仓库栈顶指针为SP),这些变量界说在DATA地址空间,并且可在文件startup.a51中进行初始化。依据编译时选用的存储器形式,模仿栈区可坐落内部(IDATA)或外部(PDATA或XDATA)存储器中。如表1所示:

存储形式

栈指针

栈区域

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//为了剖析简略,参数都是char型;

{

int j1,j2;

j1 = a + b + c +d;

j2 = j1 + 10;

return j2;

}

main()

{

int i;

i = fun(1,2,3,4);

}

程序很简略,废话少说,下面跟我一同看看c51翻译成的汇编语言是什么姿态的。

main()

{

int i;

i = fun(1,2,3,4);

MOVDPTR,#0xFFFF;模仿栈指针C?XBP开始指向0xFFFF+1

LCALLC?ADDXBP(C:00A6);调用C?ADDXBP子程序,调整模仿栈指针C?XBP

;指向0xFFFF

MOVA,#0x04;无寄存器可用,第四个参数直接压入模仿栈

MOVX@DPTR,A;

MOVR3,#0x03;参数3经过R3传递,见表2

MOVR5,#0x02;参数2过R5传递,见表2

MOVR7,#0x01;参数1经过R7传递,见表2

LCALLfun(C:0003);调用fun函数

MOVDPTR,#C_STARTUP(0x0000) ; fun函数回来值(int型)经过R6,R7传递回来

;并存储在外部数据存储器0x0000和0x0001处

;(int型为两个字节)

MOVA,R6

MOVX@DPTR,A

INCDPTR

MOVA,R7

MOVX@DPTR,A

}

RET;main回来

阐明:模仿栈指针开始在startup.a51中初始化为0xFFFF+1;由以上汇编代码能够看出参数是从右往左扫描的。

接下来看看fun的汇编代码:(很长,咱们耐性看吧,有些能够越过的)

C:0003

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6);调整模仿栈指针C?XBP=C?XBP-1

MOVA,R3

MOVX@DPTR,A;R3中的值(参数3)压入模仿栈

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6);调整模仿栈指针C?XBP=C?XBP-1

MOVA,R5

MOVX@DPTR,A;R5中的值(参数2)压入模仿栈

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6);调整模仿栈指针C?XBP=C?XBP-1

MOVA,R7

MOVX@DPTR,A;R7中的值(参数1)压入模仿栈

MOVDPTR,#0xFFFC

LCALLC?ADDXBP(C:00A6);持续调整模仿栈指针C?XBP=C?XBP-4,为放两个

;部分int变量做准备

j1 = a + b + c +d;

MOVDPTR,#0x0005

LCALLC?XBPOFF(C:00CA);经过C?XBP的值调整DPTR使其指向模仿栈中第

;一个参数,此刻DPTR=0xFFFF

;留意:C?XBPOFF不改动C?XBP的值

MOVXA,@DPTR

MOVR7,A;取出参数1

MOVA,R7

。。。。。。

。。。。。。;省掉,完结取参数2,取参数3,取参数4并相加

。。。。。。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/bandaoti/256875.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部