中断中调用函数与函数可重入问题.docx

上传人:b****6 文档编号:8613449 上传时间:2023-02-01 格式:DOCX 页数:11 大小:244.57KB
下载 相关 举报
中断中调用函数与函数可重入问题.docx_第1页
第1页 / 共11页
中断中调用函数与函数可重入问题.docx_第2页
第2页 / 共11页
中断中调用函数与函数可重入问题.docx_第3页
第3页 / 共11页
中断中调用函数与函数可重入问题.docx_第4页
第4页 / 共11页
中断中调用函数与函数可重入问题.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

中断中调用函数与函数可重入问题.docx

《中断中调用函数与函数可重入问题.docx》由会员分享,可在线阅读,更多相关《中断中调用函数与函数可重入问题.docx(11页珍藏版)》请在冰豆网上搜索。

中断中调用函数与函数可重入问题.docx

中断中调用函数与函数可重入问题

中断中调用函数与函数可重入问题

一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?

答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一些。

1g&j;^;r!

@'`    二、using的用法,using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈

7g/O  F.@%A!

k7_9的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。

同时个人建议中断函数应该使用using这个关键字。

9V8[!

t0y(l

三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是这个观点:

有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如  r5X8C;^7s2g+]:

}1u$`({厦门E城论坛果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部单独拉出来做成函数,可能代码和时间都会更好。

4C4h(A/M8[.u&Z&x.A.M6    四、中断中调用的函数最好不要被中断外的其它函数调用,因为会出现“重复调用”的警告,有时这种调用是很致命的,有人说这个函数可以用reentrant来修饰,是的,的确可以这样解决,不过个人不建议这么做,也许这样会跟你减少很多堆栈空间,并且整个程序的优化要差很多,个人建议出现这种情况就把这个函数写两遍,分成两个函数分别调用。

(o  O+L&\8b6E3O.Z"e:

I厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,    五,中断调用了函数,会出现一些莫名其妙的问题,一些数据不对。

其实一般是因为汇编中使用了绝对寄存器引起的,有人说中断函数使用那个寄存器组,被中断调用的函数就使用哪个寄存器组,我认为这样不好:

厦门E城论坛6s8B5J9?

;n:

|"c%f

这样会增加额外的消耗,使用using会增加一下语句:

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识1d)G.l(W,G*g0O$G

PUSHPSW7s(N)q,w  j$o$S+f2p5o

MOVPSW,#XX

9K3i/A2[-E:

w;V-]!

I5Y0P    ....

&d9D3S5K0`4V厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,    POPPSW-z.L+`'_8}1B)A,a

更重要的是,使用using的函数不能有返回值,这是致命伤

'v'g#e6W*D:

|"Q0g厦门E城论坛    个人推荐的方法有两种:

"V(Q  P#^9y:

O)D2z9g厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,    1、使用“#pragmaNOAREGS”禁止使用绝对寄存器厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,'s7n1e7H:

A4c3?

+M:

_

2、使用“#pragmeRB(x)”来指定本文件的工作寄存器组4\2I+R1a#S'N

六、一般说来,要求中断函数尽可能的短,但也有特殊情况,有些前/后台的系统中,就会把很多相对重要的事情放到定时中断(这个定时中断类似实时操作系统中的时钟节拍)去做,而且程序很长。

我单独提出来这点是想告诉大家,中断函数8j7^8O:

f  V$J

也是一个函数而已,只要系统有必要,可以做一些看似不合理的事情,该出手时就出手,就像goto语句一样。

PC中可重入函数

7v3H3N5~)f8Z主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,n&|&e,}9H"\"w$K.d8w也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。

如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。

可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识"J6X6f8y,y5r*E's#J8C1I编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。

(J!

g#a(^4\,|/n0e%d6O说明:

若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

厦门E城论坛:

_2_1w!

h1K$d-F#\"i5U示例:

假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。

那么如下函数不具有可重入性。

3aT*f1I,o:

F)F5sunsignedintexample(intpara){集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识;g2S!

c2t*H1W$D8Runsignedinttemp;3N,j9j*S.W-u!

J厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,Exam=para;//(**)2x;\7x!

X-M.^5Btemp=Square_Exam();'c1k&y'p,S5V*l!

l,ereturntemp;!

v-f+H{9@7Z4}-y厦门E城论坛}%M.X/k+O5~9~7d:

X此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp=Square_Exam()”后,计算出的temp很可能不是预想中的结果。

此函数应如下改进。

0M7U,|'t8`)i.j/m$wunsignedintexample(intpara){0G&qW/N)K-K"cunsignedinttemp;'s)C!

M%F0V!

s[申请信号量操作]//

(1).e)N%k4d3_/~厦门E城论坛Exam=para;'a/V6S-?

5D-w5j+z!

{6d厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,temp=Square_Exam();集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识n0g!

k8?

%Y1T.I/\)O6~[释放信号量操作]3J)P&N/i$o(mreturntemp;5S*Q#j7n3j-H;h}厦门E城论坛'f9v6q6G/l!

W!

I/S"H

(1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。

若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。

厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,0Q.C8M/a!

_*N保证函数的可重入性的方法:

厦门E城论坛7Q)Y9@#Z8d6m在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(如采取关中断、信号量等方法),这样构成的函数就一定是一个可重入的函数。

#I#L(j%}8l%|3m;b'MVxWorks中采取的可重入的技术有:

;o3il/]7R;kN厦门E城论坛*动态堆栈变量(各子函数有自己独立的堆栈空间)集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识'c0H%q0_(c:

[!

M7h.F$}*受保护的全局变量和静态变量p2j)b(]7e4v/t,]_*任务变量.L0?

%n+]4U在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。

如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。

那么什么是可重入函数呢?

所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。

不可重入函数在实时系统设计中被视为不安全函数。

满足下列条件的函数多数是不可重入的:

[-`9G3T!

o#e/B1)函数体内使用了静态的数据结构;6s&N;a"R$},M2)函数体内调用了malloc()或者free()函数;(h"f)z*`!

R厦门E城论坛3)函数体内调用了标准I/O函数。

h8}:

N)C/H下面举例加以说明。

0E+p5@4q:

U8S7t3[5g)j2z厦门E城论坛A.可重入函数%\+h.q:

E$T4Fvoidstrcpy(char*lpszDest,char*lpszSrc){:

i"j$F6@2j4Y!

b"`'e&Ewhile(*lpszDest++=*lpszSrc++);-S-|9^;r4H.R:

S0_*dest=0;.F/Yi5O%n0\6v7_}(z-?

!

e8e'Q)]#L+Q,?

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识B.不可重入函数1)`#x$r-c&u"t6a1T&^厦门E城论坛charcTemp;//全局变量)d3r&e'h4OvoidSwapChar1(char*lpcX,char*lpcY){-g7a'w*N,a;s-^'`-U厦门E城论坛cTemp=*lpcX;3{/@9v8D7I9x2a9KL!

f厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,*lpcX=*lpcY;"L4N8E3i.K|6s*l3XlpcY=cTemp;//访问了全局变量集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识4D*s6~x2z}9K3[(l5d9H!

?

C.不可重入函数(C6v's*l8?

.j0I#}*\4`voidSwapChar2(char*lpcX,char*lpcY){']8@/_1z8Z:

n&[-{5OstaticcharcTemp;//静态局部变量6?

6@$]2Y6A2d5C$s厦门E城论坛cTemp=*lpcX;-@.i/z"U5f5b*lpcX=*lpcY;)Y6H$h0N,U3I6x集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识lpcY=cTemp;//使用了静态局部变量:

z"v?

)b)mO厦门E城论坛}2~4}2]$|.e0o集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识问题1,如何编写可重入的函数?

(x&@#r&W$_#K/_:

H答:

在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。

如果必须访问全局变量,记住利用互斥信号量来保护全局变量。

)h&J5t6P)X"X5d%m集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识问题2,如何将一个不可重入的函数改写成可重入的函数?

2|(q,j7N:

l1Y0^/y厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,答:

把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写它。

其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识.x*O2L^3|1b1)不要使用全局变量。

因为别的代码很可能覆盖这些变量值。

#b7l/D*K9`#I-m)H&v)SP$|厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,2)在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。

完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”。

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识/}1ap4u)J,B/n3)不能调用其它任何不可重入的函数。

厦门E城论坛,N4\.P/V*x.S3p0\!

^3I4)谨慎使用堆栈。

最好先在使用前先OS_ENTER_KERNAL。

厦门E城论坛0V$o0^$M+E堆栈操作涉及内存分配,稍不留神就会造成益出导致覆盖其他任务的数据,所以,请谨慎使用堆栈!

最好别用!

很多黑客程序就利用了这一点以便系统执行非法代码从而轻松获得系统控制权。

还有一些规则,总之,时刻记住一句话:

保证中断是安全的!

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识1{0Z$k(A#R)B%c*a%V;y:

B8L实例问题:

曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?

厦门E城论坛8A.z2Q'B"G1R;Ounsignedintsum_int(unsignedintbase){厦门E城论坛,D(hQ'F!

k0D9Bunsignedintindex;R;^5n3F1Gstaticunsignedintsum=0;//注意,是static类型'[{7p/H.tfor(index=1;index<=base;index++)$H;g*u4]-X(z8t.Ssum+=index;e7s!

N)W%rreturnsum;6N6V,C9W#q.K#g$Q}厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,8w2g/a7i.P;^6]*}分析:

所谓的函数是可重入的(也可以说是可预测的),即只要输入数据相同就应产生相同的输出。

这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:

带“内部存储器”功能的的函数。

因此如果需要一个可重入的函数,一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。

集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识!

N9e/Q7@&q)w'D将上面的函数修改为可重入的函数,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto类型的变量,函数即变为一个可重入的函数。

+I)q-a/_+P5n7W集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。

 

Keil中可重入函数与PC的异同

KEILC51可重入函数及模拟栈浅析集合厦门地区电子爱好者的论坛,互相交流,提高自己的知识5W(j4o8e3M7S9C;d厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,9l*z4A7I2`9r4k摘要:

本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。

(R2R-{Z+]!

lt,h8{1、关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)厦门E城论坛.])S:

_+Q5Z.@'u%e)b$]“可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。

可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。

”(摘自嵌入式实时操作系统uC/OS-II)2s9D#F9w%n(y6p在理解上述概念之前,必须先说一下keilc51的“覆盖技术”。

(采用该技术的原因请看附录中一网友的解释).Y/s1\"h$m#L

(1)局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况);$S%E&o:

I-v7S%X

(2)在编译链接时,即已经完成局部变量的定位;"K*y9^5c'i5g/n(3)如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。

厦门E城论坛y1p$[7W6`&y-P正是由于以上的原因,在KeilC51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重入的。

举个例子:

7V+d$f'|!

{"M+|)\厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,

在上面的代码中,TaskA与TaskB并不存在直接或间接的调用关系,因而它们的局部变量a与b便是可以被互相覆盖的,即它们可能都被定位于某一个相同的RAM空间。

这样,当TaskA运行一段时间,改变了a后,TaskB取得CPU控制权并运行时,便可能会改变b。

由于a和b指向相同的RAM空间,导致TaskA重新取得CPU控制权时,a的值已经改变,从而导致程序运行不正确,反过来亦然。

另一方面,func()与TaskB有直接的调用关系,因而其局部变量b与c不会被互相覆盖,但也不能保证func的局部变量c不会与TaskA或其他任务的局部变量形成可覆盖关系。

0P:

yF/H-H"j%s6NZ根据上述分析我们很容易就能够判断出TaskA和TaskB这两个函数是不可重入的(当然,func也不可重入)。

那么如何让函数成为可重入函数呢?

C51编译器采用了一个扩展关键字reentrant作为定义函数时的选项,需要将一个函数定义为可重入函数时,只要在函数后面加上关键字reentrant即可。

厦门E城论坛.z'V-\0L/Z2A.G)j与非可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为可重入函数生成一个模拟栈(相对于系统堆栈或是硬件堆栈来说),通过这个模拟栈来完成参数传递和存放局部变量。

模拟栈以全局变量?

C_IBP、?

C_PBP和?

C_XBP作为栈指针(系统堆栈栈顶指针为SP),这些变量定义在DATA地址空间,并且可在文件startup.a51中进行初始化。

根据编译时采用的存储器模式,模拟栈区可位于内部(IDATA)或外部(PDATA或XDATA)存储器中。

如表1所示:

$o8w#q)m.r.h7L'y4W:

z厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,

表1

s.h;j5s)j-_4@厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,9A/c6`2`6M4|注意:

51系列单片机的系统堆栈(也叫硬件堆栈或常规栈)总是位于内部数据存储器中(SP为8位寄存器,只能指向内部),而且是“向上生长”型的(从低地址向高地址),而模拟栈是“向下生长”型的。

g!

c)@%u(z)M/r:

x7w1、可重入函数参数传递过程剖析;T0T$T3J.R#e在进入剖析之前,先简单讲讲c51函数调用时参数是如何传递的。

简单来说,参数主要是通过寄存器R1~R7来传递的,如果在调用时,参数无寄存器可用或是采用了编译控制指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。

利用51单片机的工作寄存器最多传递3个参数,如表2所示。

7f:

W6v'E7I#H6q#e$h5L厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,

表二

!

W2b#b6L0A,p.n+D0N+f2J2X'].j举两个例子:

厦门E城论坛6}0W(O:

|-Vfunc1(inta):

“a”是第一个参数,在R6,R7中传递;*Tt)^*f0T"e7n.a7}厦门E城论坛func2(intb,intc,int*d):

“b”在R6,R7中传递,“c”在R4,R5中传递,“*d”则在R1,R2,R3中传递。

厦门电子城,厦门e城,电子零件,单片机,电子,xmecity,电子制作,DIY,电脑,元件,2t,B$L$b9D+z7U0Px!

G0r至于函数的返回值通过哪些寄存器或是什么方法传递这里就不说了,大家可以看看c51的相关文档或是书籍。

"P$C%YC0B8V2?

好了,接下来我们

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1