一、可重入函数

合集下载

单片机程序实现可重入和不可重入函数方法

单片机程序实现可重入和不可重入函数方法

单片机程序实现可重入和不可重入函数方法
可重入函数是指一个函数可以被多个任务同时调用而不会产生冲突的函数,不可重入函数是指一个函数在被同时调用时可能会产生冲突的函数。

实现可重入函数的方法如下:1. 避免使用全局变量和静态变量。

全局变量和静态变量可能会在函数调用期间被修改,导致并发访问时出现冲突。

如果需要使用全局变量,可以使用互斥锁保护对全局变量的访问。

2. 使用局部变量。

局部变量是每次函数调用时创建的,不会被其他任务访问到,因此可以保证并发访问的安全性。

3. 使用栈上的变量。

栈上的变量是每次函数调用时分配的,不会被其他任务访问到,因此可以保证并发访问的安全性。

4. 避免使用非可重入函数。

如果需要使用非可重入函数,可以使用互斥锁保护对非可重入函数的访问。

实现不可重入函数的方法如下:1. 使用全局变量和静态变量。

全局变量和静态变量是多个任务共享的,如果在函数中使用全局变量和静态变量,可能会导致并发访问时出现冲突。

2. 使用非线程安全的库函数。

一些库函数在多任务环境下可能不是线程安全的,如果在函数中使用这些库函数,可能会导致并发访问时出现冲突。

3. 使用非原子操作的指令。

一些指令在执行时可能是非原子的,如果在函数中使用这些指令,可能会导致并发访问时出现冲突。

需要注意的是,可重入函数不一定是线程安全的,因为线程安全还需要考虑共享资源的访问和同步。

而不可重入函数通常也不是线程安全的,因为它可能会对全局资源进行修改,需要使用互斥锁或其他同步
机制来保护对全局资源的访问。

KeilC51可重入函数reentrantfunction和递归分析

KeilC51可重入函数reentrantfunction和递归分析

KeilC51可重⼊函数reentrantfunction和递归分析可重⼊与不可重⼊,reentrant关键字Keil中帮助⽂档对此⼜详细的介绍这⼀段的意思是,在Keil中,正常情况下函数调⽤是通过固定寄存器传递参数。

因此当出现递归和类似的情况时,寄存器中参数可能会被覆盖。

如果想要通过堆栈来传递参数则需要使⽤reentrant参数。

这个⽤来传递参数的堆栈叫⽅针堆栈,与硬件堆栈不同。

这⼀段的意思是,在Keil中,正常情况下函数调⽤是通过固定寄存器传递参数。

因此当出现递归和类似的情况时,寄存器中参数可能会被覆盖。

如果想要通过堆栈来传递参数则需要使⽤reentrant参数。

这个⽤来传递参数的堆栈叫⽅针堆栈,与硬件堆栈不同。

如上图,不同的存储模式下,仿真堆栈的名字是不⼀样的。

因此,系统⾥⾯就会存在2个堆栈。

⼀个是系统⽤来传递参数的堆栈,如图中的?C_XBP,⽤DPTR寄存器进⾏操作。

(再次强调,若没有定义reentrant,参数的传递通过R1-R7寄存器进⾏)。

另外⼀个是⼤家⽐较熟悉的系统⽤来维护函数调⽤与中断调⽤等情况下压栈和出栈操使⽤的堆栈。

如图中的?STACK。

⽤SP寄存器进⾏操作。

下⾯结合具体的实例,分析递归调⽤时两个堆栈的情况。

如果不定义reentrant这个关键字,则编译时会出现如下错误,很显然参数在传递时会因为被覆盖导致表达式计算不是真是想要的。

当n=1时,第⼀次调⽤。

此时keil会将参数n存放⼊ R6和R7中。

调⽤mul函数,此时函数的返回地址会被放⼊?Stack中,该堆栈从0x0A开始(由系统分配,因此在编程时⼀定要注意该堆栈的⼤⼩)进⼊mul函数,此时会将存在R6与R7中的参数保存⼊仿真堆栈中?C_XBP中,该栈从0x80往下⽣长该堆栈的⼯作⽅式为先往下腾出位置,在把值写进去。

⽽框框中的操作数0xFFFE则是补码的形式,跟踪C?ADDXBP可以看出,该语句就是将堆栈往下移动⼀个空间当需要对该参数进⾏操作时,会从堆栈中取出参数进⾏操作⾄此,可以看出增加了reentrant关键字之后,其对局部变量的操作都是在堆栈中完成,这也是能够完成函数重⼊的关键。

可重入函数

可重入函数

占先式(Preemptive)
低优先级任务 中断服务程序使 高优先级任务就绪
(1)
(2)
ISR
(5)
(3)
高优先级任务
TIME
(4)
(6)
高优先级任务得到 CPU使用权
可重入型函数
可以被一个以上的任务调用,而不必担心数据的破坏。可重 入型函数任何时候都可以被中断,一段时间以后又可以运行,而相 应数据不会丢失。可重入型函数只使用局部变量,即变量保存在 CPU寄存器中或堆栈中。 一个不可重入型函数的例子
什么是可重入代码
• 可重入的代码指的是一段代码(比 如:一个函数)可以被多个任务同 时调用,而不必担心会破坏数据。
• 也就是说,可重入型函数在任何时 候都可以被中断执行,过一段时间 以后又可以继续运行,而不会因为 在函数中断的时候被其他的任务重 新调用,影响函数中的数据。
可重入代码举例
程序1:可重入型函数 void swap(int *x, int *y) { int temp; temp=*x; *x=*y; *y=temp; }
占先式(preemptive)
当系统响应时间很重要时,要使用占先式 (preemptive)内核。最高优先级的任务一旦 就绪,总能得到CPU的控制权。 当一个运行着的任务使一个比它优先级高 的任务进入了就绪态,当前任务的CPU使用权 就被剥夺了,或者说被挂起了,那个高优先级 的任务立刻得到了CPU的控制权。 使用占先式内核时,应用程序不应直接使 用不可重入型函数。如果调入不可重入型函数 时,低优先级的任务CPU的使用权被高优先级 任务剥夺,不可重入型函数中的数据有可能被 破坏。
非可重入代码举例
程序2:非可重入型函数 int temp; void swap(int *x, int *y) { temp=*x; *x=*y; *y=temp; 返回 }

KEILC51可重入函数及模拟栈浅析

KEILC51可重入函数及模拟栈浅析

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

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

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

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

(采用该技术的原因请看附录中一网友的解释)(1)局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况);(2)在编译链接时,即已经完成局部变量的定位;(3)如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。

正是由于以上的原因,在Keil C51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重入的。

举个例子:在上面的代码中,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 即可。

函数的可重入性、线程安全函数、异步信号安全函数

函数的可重入性、线程安全函数、异步信号安全函数

函数的可重⼊性、线程安全函数、异步信号安全函数 重⼊即表⽰重复进⼊,⾸先它意味着这个函数可以被中断,其次意味着它除了使⽤⾃⼰栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重⼊,可以允许有该函数的多个副本在运⾏,由于它们使⽤的是分离的栈,所以不会互相⼲扰。

,常见的情况是,程序执⾏到某个函数foo()时,收到信号,于是暂停⽬前正在执⾏的函数,转到信号处理函数,⽽这个信号处理函数的执⾏过程中,⼜恰恰也会进⼊到刚刚执⾏的函数foo(),这样便发⽣了所谓的重⼊。

此时如果foo()能够正确的运⾏,⽽且处理完成后,之前暂停的foo()也能够正确运⾏,则说明它是可重⼊的。

要确保函数可重⼊,需满⾜⼀下⼏个条件: 1、不在函数内部使⽤静态或全局数据 2、不返回静态或全局数据,所有数据都由函数的调⽤者提供。

3、使⽤本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。

4、不调⽤不可重⼊函数。

线程安全:⼀个函数被称为线程安全的,当且仅当被多个并发线程反复的调⽤时,它会⼀直产⽣正确的结果。

要确保函数线程安全,主要需要考虑的是线程之间的共享变量。

在对这些共享变量进⾏访问时,如果要保证线程安全,则必须通过加锁的⽅式。

可重⼊与线程安全并不等同,⼀般说来,可重⼊的函数⼀定是线程安全的,但反过来不⼀定成⽴。

可以被信号控制器安全调⽤的函数被称为"异步信号安全"函数。

信号就像硬件中断⼀样,会打断正在执⾏的指令序列。

信号处理函数⽆法判断捕获到信号的时候,进程在何处运⾏。

如果信号处理函数中的操作与打断的函数的操作相同,⽽且这个操作中有静态数据结构等,当信号处理函数返回的时候(当然这⾥讨论的是信号处理函数可以返回),恢复原先的执⾏序列,可能会导致信号处理函数中的操作覆盖了之前正常操作中的数据。

不可重⼊函数的原因在于: 1. 已知它们使⽤静态数据结构 2.它们调⽤malloc和free.因为malloc通常会为所分配的存储区维护⼀个链接表,⽽插⼊执⾏信号处理函数的时候,进程可能正在修改此链接表。

关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)(2012-04-21 14:53:58)转载▼分类:51单片机标签:杂谈作者:xzp21st 邮箱:*****************撰文辛苦,转载请注明作者及出处关键字:keilc51,模拟堆栈,可重入函数调用,参数传递,C?XBP,C?ADDXBP摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。

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空间。

Linux中可重入函数与不可重入函数详解

Linux中可重入函数与不可重入函数详解

Linux中可重⼊函数与不可重⼊函数详解
Linux 中可重⼊函数与不可重⼊函数详解
可重⼊函数和不可重⼊函数说起来有点拗⼝,其实写过多进程(线程)程序的⼈肯定很快就能明⽩这两种函数是个神马东西。

下⾯是我对这两个函数的理解:
可重⼊函数可以理解为是能被中断的函数,并且它被中断返回后也不会出现什么错误。

不可重⼊函数可以理解为如果函数被中断的话,就会出现不可预料的错误。

这是因为函数中使⽤了⼀些系统资源,⽐如全局变量区,中断向量表之类的。

⽐如多个进程同时对⼀个⽂件进⾏写操作,如果没有同步机制的话,对⽂件的写⼊就会变得难以控制。

在多进程(线程)环境中⼀定要考虑到函数的可重⼊性。

例如下⾯的例⼦:
int sum( int count)
{
static int sum = 0;
int i = 0;
for (i= 1; index <= count; i++)
sum += i;
return sum;
}
这段代码中使⽤了static关键字,如果多个进程同时执⾏这⼀段代码的话就会出现不可预测的结果。

在可重⼊函数中,⼀定要避免使⽤static变量。

或者需要使⽤⼀定的同步原则才可以。

要想将上⾯的代码改为可重⼊函数,只要将static变量改为⾮static的变量就可以了。

在编写可重⼊函数时,如果函数中使⽤到了全局变量,则应通过关中断、信号量(即P、V操作)等⼿段对其加以保护,在函数体中尽量使⽤局部变量。

感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!。

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

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

中断中调用函数与函数可重入问题一、中断函数是一个特殊的函数,没有参数,也没有返回值;但是程序中允不允许使用return呢?答案是允许的,不过只能用"return;",不能用"return(z);";用在一些需要快速返回的地方,对应的汇编会有多个ret语句,相对效率会高一些。

1 g& j; ^; r! @' ` 二、using的用法,using可以修饰任何函数,不过个人建议只用来修饰中断函数;简单的说,“using”会指定工作寄存器组,由于中断函数一般都是比较紧急的事情,有时一条语句都会斤斤计较,所以使用using切换寄存器组可以省去一些压栈7 g/ O F. @% A! k7 _9 的动作,由于51只有两级中断,同级中断不能被打断,因此,我们可以同级中断设成同样的寄存器组,从某种意义上来说,有一组寄存器是多余的。

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

9 V8 [! t0 y( l三、中断中调用函数,首先要讨论中断函数中调用函数的必要性,前天在论坛上我和别人争论过这个问题,现在我还是这个观点:有些情况中断中调用函数还是必要的,这个时候是不是该调用函数,其实和普通函数差不多,首先是这个函数如r5 X8 C; ^7 s2 g+ ]: }1 u$ `( {厦门E城论坛果调用多次,或者要带一些参数什么的就更加必要的;前天有人跟我叫劲,说假如只调用一次且无参数无返回的函数要直接写,因为如果用函数,至少会增加CALL和RET两条语句,我不敢苟同,我是实际调试发现的,当你程序比较复杂时,你将那部单独拉出来做成函数,可能代码和时间都会更好。

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

函数可重入性及编写规范2

函数可重入性及编写规范2

函数可重入性及编写规范(2)[作者:佚名转贴自:网络点击数:360 更新时间:2007-1-23 文章录入:huitong ]上接函数可重入性及编写规范112 :尽量不要编写依赖于其他函数内部实现的函数说明:此条为函数独立性的基本要求。

由于目前大部分高级语言都是结构化的,所以通过具体语言的语法要求与编译器功能,基本就可以防止这种情况发生。

但在汇编语言中,由于其灵活性,很可能使函数出现这种情况。

示例:如下是在DOS下TASM的汇编程序例子。

过程Print_Msg的实现依赖于Input_Msg的具体实现,这种程序是非结构化的,难以维护、修改。

... // 程序代码proc Print_Msg // 过程(函数)Print_Msg... // 程序代码jmp LABEL... // 程序代码endpproc Input_Msg // 过程(函数)Input_Msg... // 程序代码LABEL:... // 程序代码endp13 :避免设计多参数函数,不使用的参数从接口中去掉说明:目的减少函数间接口的复杂度。

14 :非调度函数应减少或防止控制参数,尽量只使用数据参数说明:本建议目的是防止函数间的控制耦合。

调度函数是指根据输入的消息类型或控制命令,来启动相应的功能实体(即函数或过程),而本身并不完成具体功能。

控制参数是指改变函数功能行为的参数,即函数要根据此参数来决定具体怎样工作。

非调度函数的控制参数增加了函数间的控制耦合,很可能使函数间的耦合度增大,并使函数的功能不唯一。

示例:如下函数构造不太合理。

int add_sub( int a, int b, unsigned char add_sub_flg ){if (add_sub_flg == INTEGER_ADD){return (a + b);}else{return (a b);}}不如分为如下两个函数清晰。

int add( int a, int b ){return (a + b);}int sub( int a, int b ){return (a b);}15 :检查函数所有参数输入的有效性16 :检查函数所有非参数输入的有效性,如数据文件、公共变量等说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。

可重入问题

可重入问题

一、可重入函数1)什么是可重入性?可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。

相反,不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。

可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。

可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据。

2)可重入函数:不为连续的调用持有静态数据。

不返回指向静态数据的指针;所有数据都由函数的调用者提供。

使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。

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

绝不调用任何不可重入函数。

3)不可重入函数:函数中使用了静态变量,无论是全局静态变量还是局部静态变量。

函数返回静态变量。

函数中调用了不可重入函数。

函数体内使用了静态的数据结构;函数体内调用了malloc()或者free()函数;函数体内调用了其他标准I/O函数。

函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量。

总的来说,如果一个函数在重入条件下使用了未受保护的共享的资源,那么它是不可重入的。

4)示例在多线程条件下,函数应当是线程安全的,进一步,更强的条件是可重入的。

可重入函数保证了在多线程条件下,函数的状态不会出现错误。

以下分别是一个不可重入和可重入函数的示例://c codestatic int tmp;void func1(int* x, int* y) {tmp=*x;*x=*y;*y=tmp;}void func2(int* x, int* y) {int tmp;tmp=*x;*x=*y;*y=tmp;}func1是不可重入的,func2是可重入的。

因为在多线程条件下,操作系统会在func1还没有执行完的情况下,切换到另一个线程中,那个线程可能再次调用func1,这样状态就错了。

可重入函数的特点

可重入函数的特点

可重入函数的特点及其定义可重入函数是指在多线程环境下,函数可以被多个线程同时调用而不会引起不确定的结果。

它具有以下几个特点:1.线程安全:可重入函数在多线程环境下可以安全地被多个线程同时调用,不会出现数据竞争和不确定的结果。

2.可嵌套调用:可重入函数可以在自身的执行过程中再次调用自身,而不会出现死锁或其他问题。

3.无副作用:可重入函数不会对全局变量或静态变量进行修改,也不会引入全局状态,保证了函数的独立性和可重复使用性。

可重入函数的定义通常有以下几个要求:1.函数内部不使用全局变量:为了保证函数的可重入性,函数内部不应该依赖于全局变量或静态变量,而应该将所有需要的数据作为参数传入函数。

2.不修改静态变量:静态变量在函数调用过程中会被多个线程共享,如果可重入函数修改了静态变量,就会导致不确定的结果。

3.使用局部变量或栈上内存:为了避免线程间的数据竞争,可重入函数应该使用局部变量或栈上内存来保存临时数据。

4.使用信号量或互斥锁:为了保证多个线程之间的同步与互斥,可重入函数可以使用信号量或互斥锁来保护临界区。

可重入函数的用途可重入函数在多线程编程中具有重要的作用,它可以保证多个线程同时调用函数时不会出现竞争条件或数据不一致的问题。

下面是几个可重入函数的常见用途:1.管理共享资源:在多线程环境下,多个线程可能会同时访问共享资源,如内存、文件等。

可重入函数可以用来管理共享资源的访问,保证每个线程能够正确地访问资源而不会引起冲突。

2.实现线程安全的数据结构:可重入函数可以用来实现线程安全的数据结构,如队列、栈、链表等。

多个线程可以同时对数据结构进行操作,而不会导致数据结构的破坏或不一致。

3.处理信号:在多线程程序中,信号的处理通常是一个复杂的问题。

可重入函数可以用来处理信号,保证在信号处理函数中不会引起竞争条件或不确定的结果。

4.实现线程安全的库函数:可重入函数可以用来实现线程安全的库函数,使得库函数可以在多线程环境下被安全地调用。

CC++之什么是可重入函数和不可重入函数

CC++之什么是可重入函数和不可重入函数

CC++之什么是可重⼊函数和不可重⼊函数可重⼊函数在实时系统的设计中,经常会出现多个任务调⽤同⼀个函数的情况。

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

那么什么是可重⼊函数呢?所谓可重⼊是指⼀个可以被多个任务调⽤的过程,任务在调⽤时不必担⼼数据是否会出错。

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

满⾜下列条件的函数多数是不可重⼊的:(1)函数体内使⽤了静态的数据结构;(2)函数体内调⽤了malloc()或者free()函数;(3)函数体内调⽤了标准I/O函数。

如何写出可重⼊的函数?在函数体内不访问那些全局变量,不使⽤静态局部变量,坚持只使⽤缺省态(auto)局部变量,写出的函数就将是可重⼊的。

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

或者调⽤该函数前关中断,调⽤后再开中断。

可重⼊函数可以被⼀个以上的任务调⽤,⽽不必担⼼数据被破坏。

可重⼊函数任何时候都可以被中断,⼀段时间以后⼜可以运⾏,⽽相应的数据不会丢失。

可重⼊函数或者只使⽤局部变量,即保存在CPU寄存器中或堆栈中;或者使⽤全局变量,则要对全局变量予以保护。

说法2:⼀个可重⼊的函数简单来说,就是:可以被中断的函数。

就是说,你可以在这个函数执⾏的任何时候中断他的运⾏,在任务调度下去执⾏另外⼀段代码⽽不会出现什么错误。

⽽不可重⼊的函数由于使⽤了⼀些系统资源,⽐如全局变量区,中断向量表等等,所以他如果被中断的话,可能出现问题,所以这类函数是不能运⾏在多任务环境下的。

基本上下⾯的函数是不可重⼊的(1)函数体内使⽤了静态的数据结构;(2)函数体内调⽤了malloc()或者free()函数;(3)函数体内调⽤了标准I/O函数。

把⼀个不可重⼊函数变成可重⼊的唯⼀⽅法是⽤可重⼊规则来重写他。

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

可重入函数

可重入函数

可重入函数
可重入函数是指:
1、它允许一个函数在已经开始其执行的情况下,再次被其本身调用。

2、把可重入函数等价于递归函数,它们都允许在函数体内通过调用它本身来实现循环。

不同的是,可重入函数的递归调用可以用多种可重入方式交叉进行,而不必等待前一调用结束后再开始新的调用。

3、可重入函数可提高程序实现的灵活性,不必额外定义变量来实现循环,而是在函数内部调用整个函数自身,它也可以实现多种重叠功能。

4、可重入函数可用于提供多种重叠性质编程,例如大量的并发任务处理、fork/join模式或服务器/客户端系统。

5、可重入函数可以更加复杂的编写,可以实现更复杂的类似位运算的实现,也可以通过可重入函数来处理复杂的控制,包括以树形结构实现的控制。

6、此外,可重入函数还可用于实现逐步累加,这样在编译器优化时可以提高运行效率,这也是和普通循环不同的地方。

7、可重入函数的另一个优点是它可以使程序更加稳定,因为也可以用它来进行错误控制,一旦出现错误,则能直接把调用过程重复执行,从而实现异常情况的处理。

最后,可重入函数还可以大大减少函数的代码量,并且使代码可重用性更强,使得程序更小巧、代码更加紧凑,更容易理解和维护。

KEIL可从入函数讲解

KEIL可从入函数讲解

《KEIL C51可重入函数及模拟栈浅析》一文的理解此片文章主要是关于前辈文章的理解,没有超越前辈的地方,前辈文章链接如下:/lyb1900/item/404f12f49176f2b730c1995c一下是我对于这篇文章的理解,如果有错希望大家能够指出并改正,鄙人QQ:384710930在进入代码阅读之前,本人想说说我的见解:本人觉得先读C?ADDXBP和C?XBPOFF函数对于理解该文有比较好效果,但是鉴于前辈的顺序不是如此,所以我就不加更改!关键在于下面两个函数!文字功底不行,请大家见谅,一下附录原文和前辈文章里没有写全的代码!附录(原文):KEIL C51可重入函数及模拟栈浅析(转)原文:/shuqianyan/blog/static/74740421200911594540643/摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。

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

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

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

(采用该技术的原因请看附录中一网友的解释)(1)局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况);(2)在编译链接时,即已经完成局部变量的定位;(3)如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。

正是由于以上的原因,在Keil C51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重入的。

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

对setcurrentcelladdresscore函数的可重入调用

对setcurrentcelladdresscore函数的可重入调用

对setcurrentcelladdresscore函数的可重入调用在多线程编程中,函数的可重入性是一个重要的概念。

可重入函数是指可以被多个线程同时调用的函数,而不会影响程序的正确性。

setcurrentcelladdresscore函数是一种常见的可重入函数,用于设置当前单元格的地址评分。

一、函数概述setcurrentcelladdresscore函数用于为一个单元格设置地址评分。

该函数通常接受一个单元格地址和评分值作为参数,并将评分值存储在相应的单元格中。

由于该函数可以被多个线程同时调用,因此它是可重入的。

二、可重入性特点可重入函数具有以下特点:1. 函数不会被修改,因此在多线程环境中不会出现冲突。

2. 函数可以多次调用,即具有重入性。

3. 函数的局部变量和参数是私有的,不会被其他线程访问或修改。

setcurrentcelladdresscore函数符合这些特点,因为它不会修改自身的代码或数据,并且局部变量和参数是私有的,不会被其他线程访问或修改。

三、调用方式1. 直接调用:可以在任何需要设置单元格地址评分的线程中直接调用该函数。

2. 线程安全调用:可以使用线程安全的函数调用库或机制来调用该函数,以确保多个线程同时调用时不会出现冲突。

3. 传递锁:可以在调用setcurrentcelladdresscore函数之前,传递一个锁对象作为参数,以确保在调用期间只有一个线程能够访问该函数。

无论采用哪种方式,都需要确保setcurrentcelladdresscore函数的调用是线程安全的,以避免出现竞态条件或其他并发问题。

四、注意事项在可重入调用的过程中,需要注意以下几点:1. 确保局部变量和参数的正确性:setcurrentcelladdresscore 函数的局部变量和参数应该是私有的,并且在使用前已经正确初始化。

2. 避免对共享资源的访问:如果存在共享资源,需要确保在多线程环境中对这些资源进行适当的同步和保护。

可重入程序与不可重入程序的区分

可重入程序与不可重入程序的区分

可重⼊程序与不可重⼊程序的区分可重⼊性的英⽂关键词为Reentrancy,这⾥⾸先要区分可抢占这⼀概念。

在计算机⾥⾯,程序的运⾏可以说是⼀堆机器指令被放⼊CPU 进⾏顺序执⾏,但是操作系统为了更好地管理程序,就出现了各式各样的载体概念,⽐如说进程、线程、任务,究其本质,都是相对于“调度”这个操作,它们只是调度的粒度不⼀样。

如果我们在Linux或者windows下运⾏某个程序,它会依托于进程或者任务载体,由操作系统的内核对它进⾏调度执⾏。

可抢占式说明当前你执⾏的程序(任务)可以被其它程序抢占,意味着暂时失去对CPU资源的拥有。

⼀旦涉及到调度,就需要对任务之间进⾏同步和互斥了,⽐如常见的触发调度的技术如中断、任务睡眠、内核抢占等。

我在这⾥举的例⼦是可重⼊型函数,这也是可重⼊性的基础,其它引申出来的概念或者应⽤都是建⽴在它上⾯的。

可重⼊型函数任何时候都可以被中断,⼀段时间以后⼜可以运⾏,⽽相应数据不会丢失。

可重⼊型函数只使⽤局部变量,即变量保存在CPU寄存器中或栈中。

1void strcpy(char *dest, char *src)2 {3while (*dest++ = *src++) {4 (void)0;5 }6 *dest = NUL;7 }strcpy()是属于可重⼊型函数,多个任务或者线程调⽤它之后,并不会带来同步等问题。

如果说某个函数使⽤的变量涉及到全局变量或者堆,这样的函数就可能是不可重⼊型函数了,有可能得解决资源互斥的问题。

在嵌⼊式研发中,可以利⽤开关中断、任务优先级调整,信号量,⾃旋锁等多种⼿段来解决不可重⼊的问题。

举个常见的例⼦,使⽤全局变量的函数也并⾮⼀定是不可重⼊的,例如该全局变量已经被赋值且只读性质。

再举⼀个实际例⼦,某个函数的内部并不是所有代码都是不可重⼊的,如果有相当部分代码并不涉及资源互斥,这时候就可以采⽤加锁解锁的⽅式来进⾏任务之间同步,达到逻辑意义上的可重⼊。

可再入函数

可再入函数
因为8051 内部堆栈空间的限制,C51 没有像大系统那样使用调用堆栈。一般C 语言中调用过程的参数和过程中使用的局部变量入栈。为了提高效率,防备函数堆栈大量占用有限的数据区
C51 没有提供这种堆栈,而是提供一种压缩型式的堆栈。每个过程被给定一个空间用于存放局部变量,过程中的每个变量都存放在这个空间的固定位置,当递归调用这个过程时,会导致变量被覆盖。
a、在相应的函数前使用前述“#pragma disable”声明,即只允许主程序或中断之一调用该函数;
b、将该函数说明为可重入的。如:void func(param...) reentrant;
KeilC51编译后将生成一个可重入变量堆栈,然后就可以模拟通过堆栈传递变量的方法。由于一般可重入函数由主程序和中断调用,所以通常中断使用与主程序不同的R寄存器组。
可重入函数的说明: void func(param...) reentrant;
KeilC51编译器认为函数缺省都是可重入的。
在主程序和中断中都可调用的函数,容易产生问题。因为51和PC不同,PC使用堆栈传递参数,且静态变量以外的内部变量都在堆栈中;而51一般使用寄存器传递参数,内部变量一般在RAM中,函数重入时会破坏上次调用的数据。可以用以下两种方法解决函数重入:
** 函数原型: bit CAN_CMD_PRG(uchar cmd)
** 功能描述: 执行sja1000命令
** 参数说明: cmd:sja1000运行的命令字
** 01:发送请求
** 02:中止发送
** 04:释放接收缓冲区
** 08:清除超载状态
** 0x10:进入睡眠状态
** 返回值: 1 执行sja1000命令 成功

单片机程序实现可重入和不可重入函数方法

单片机程序实现可重入和不可重入函数方法

单片机程序实现可重入和不可重入函数方法(最新版3篇)篇1 目录一、单片机程序实现可重入和不可重入函数方法的概述1.可重入函数和不可重入函数的概念2.单片机程序实现可重入和不可重入函数方法的意义3.实现可重入和不可重入函数方法的常用方法二、单片机程序实现可重入函数的详细步骤1.函数的可重入性设计原则2.函数参数的传递方式3.函数内部的变量定义和使用4.函数调用时的上下文切换和保存5.函数的返回值和异常处理三、单片机程序实现不可重入函数的详细步骤1.函数的不可重入性设计原则2.函数参数的传递方式3.函数内部的变量定义和使用4.函数调用时的上下文切换和保存5.函数的返回值和异常处理篇1正文一、单片机程序实现可重入和不可重入函数方法的概述1.可重入函数和不可重入函数的概念:可重入函数是指在多线程或多任务环境下,可以被多次调用的函数;不可重入函数则是指不能被多次调用的函数,因为多次调用会导致死锁或数据竞争等问题。

2.单片机程序实现可重入和不可重入函数方法的意义:在单片机系统中,多线程或多任务的情况时有发生,因此实现可重入和不可重入函数方法可以提高系统的稳定性和可靠性。

3.实现可重入和不可重入函数方法的常用方法:可采用同步机制(如锁、信号量等)来控制函数的访问权限,避免多线程或多任务之间的冲突。

同时,对于不可重入函数,需要注意避免使用共享变量或使用原子操作来保护临界区代码。

二、单片机程序实现可重入函数的详细步骤:1.函数的可重入性设计原则:可重入性设计原则包括以下几点:避免共享变量、使用同步机制、避免递归调用、避免使用全局变量等。

2.函数参数的传递方式:参数的传递方式会影响函数的可重用性和性能,因此需要选择合适的参数传递方式。

例如,如果参数是复杂数据结构,可以考虑使用传递指针的方式;如果参数是基本数据类型,可以考虑使用寄存器传递。

3.函数内部的变量定义和使用:在函数内部,需要使用局部变量来存储数据,避免使用全局变量或静态变量。

09-第九章 分布式数据分析工具Pig

09-第九章 分布式数据分析工具Pig

9.5 模式(schemas)

1. 定义关系模式

同时指定字段名称(field name)和字段类型(field type) 只指定字段名称,不指定字段数据类型(此时字段类型默 认为 bytearray)

2. 操作关系模式

(1)具有模式的数据访问方式 (2)不具有模式的数据访问方式 (3)强制类型转换 (4)自动类型转换
9.1 Pig 的安装
从网站 /releases.html 下载最新 的 Pig 稳定版本,下载后解压到相应安 装目录,解压后会生成子目录 pig-x.y.z(x.y.z 为版本 号) :
设置环境变量,编辑文件~/.bashrc 或 ~/.bash_profile 把 Pig 的可执行文件所处路径添加 到PATH 变量中,方便 Pig 的使用和管理:
92pig基本概念piglatin标识符piglatin语句93pig保留关键字pig相关命令命令选项后面可以跟简单的pig操作命令利用piglatin提供的交互式shell工具grunt可以更方便地操作pig命令pig内置函数builtinfunction1可重入函数evalfunctions2导入存储函数loadstorefunctions3数学计算函数mathfunctions4字符串处理函数stringfuntions5日期函数datetimefunctionsmap函数93pig保留关键字数据类型94使用pigpig命令行选项或execute选项或help选项properties选项或propertyfile选项pig的两种运行模式2mapreduce模式pig相关shell命令详解1外部相关shell操作命令2gruntshell内部功能命令3gruntshell与hdfs交互命令94使用pigpig程序运行方式1pig脚本文件2pig交互式shellgrunt3嵌入pig命令到宿主程序pig输入与输出3dump95模式schemas同时指定字段名称fieldname和字段类型fieldtype只指定字段名称不指定字段数据类型此时字段类型默认为bytearray用load和stream定义关系模式用foreach定义关系模式95模式schemas2为前两个字段指定别名和类型第三个字段只指定别名不指定类型3map模式为value不指定类型指定value类型为tuple4包含多种复杂类型的模式96pig相关函数详解内置函数内置函数包括可重入函数evalfunctions加载存储函数loadstorefunctions数学函数mathfunctions字符串函数stringfunctions和时间函数datetimefunctionsmapbagtuple函数等
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
即:要么是调用者和被调用者对参数均不作合法性检查
结果就遗漏了合法性检查这一必要的处理过程
造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查
这种情况虽不会造成问题
但产生了冗余代码
降低了效率
6 :防止将函数的参数作为工作变量
说明:将函数的参数作为工作变量
有可能错误地改变参数内容
则必须是STATIC的局部变量的地址作为返回值
若为AUTO类
则返回为错针
示例:如下函数
其返回值(即功能)是不可预测的
unsigned int integer_sum( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意
如果必须访问全局变量
记住利用互斥信号量来保护全局变量
绝不调用任何不可重入函数
3)不可重入函数:
函数中使用了静态变量
无论是全局静态变量还是局部静态变量
函数返回静态变量
函数中调用了不可重入函数
函数体内使用了静态的数据结构;
函数体内调用了malloc()或者free()函数;
{
unsigned int count ;
int sum_temp;
sum_temp = 0;
for (count = 0; count < num; count ++)
{
sum_temp += data[count];
19 :避免使用无意义或含义不清的动词为函数命名
说明:避免用含义不清的动词如process、handle等为函数命名
因为这些动词并没有说明要具体做什么
}
12 :尽量不要编写依赖于其他函数内部实现的函数
说明:此条为函数独立性的基本要求
由于目前大部分高级语言都是结构化的
所以通过具体语言的语法要求与编译器功能
基本就可以防止这种情况发生
但在汇编语言中
由于其灵活性
很可能使函数出现这种情况
示例:如下是在DOS下TASM的汇编程序例子
1 :对所调用函数的错误返回码要仔细、全面地处理
2 :明确函数功能
精确(而不是近似)地实现函数设计使用(如编写C/C++ 语言的可重入函数时
应使用auto 即缺省态局部变量或寄存器变量)
说明:编写C/C++语言的可重入函数时
不应使用static局部变量
int tmp;
tmp=*x;
*x=*y;
*y=tmp;
}
func1是不可重入的
func2是可重入的
因为在多线程条件下
操作系统会在func1还没有执行完的情况下
切换到另一个线程中
那个线程可能再次调用func1
这样状态就错了
二、函数编写规范
否则必须经过特殊处理
才能使函数具有可重入性
4 :编写可重入函数时
若使用全局变量
则应通过关中断、信号量(即P 、V 操作)等手段对其加以保护
说明:若对所使用的全局变量不加以保护
则此函数就不具有可重入性
即当多个进程调用此函数时
很有可能使有关全局变量变为不可知状态
示例:假设Exam是int型全局变量
并使函数的功能不唯一
示例:如下函数构造不太合理
int add_sub( int a, int b, unsigned char add_sub_flg )
{
if (add_sub_flg == INTEGER_ADD)
{
return (a + b);
函数体内调用了其他标准I/O函数
函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量
总的来说
如果一个函数在重入条件下使用了未受保护的共享的资源
那么它是不可重入的
4)示例
在多线程条件下
函数应当是线程安全的
进一步
更强的条件是可重入的
可重入函数保证了在多线程条件下
所以很危险
对必须改变的参数
最好先用局部变量代之
最后再将该局部变量的内容赋给该参数
示例:如下函数的实现就不太好
void sum_data( unsigned int num, int *data, int *sum )
{
unsigned int count;
*sum = 0;
是static类型的
// 若改为auto类型
则函数即变为可预测
for (index = 1; index <= base; index++)
{
sum += index;
}
return sum;
或改为如下
#define MAX (a, b) (((a) > (b)) ? (a) : (b))
value = MAX (a, b);
10:不要设计多用途面面俱到的函数
说明:多功能集于一身的函数
很可能使函数的理解、测试、维护等变得困难
11:函数的功能应该是可以预测的
或者在代码的关键部分禁用中断)
可重入函数可以在任意时刻被中断
稍后再继续运行
不会丢失数据
可重入函数要么使用本地变量
要么在使用全局变量时保护自己的数据
2)可重入函数:
不为连续的调用持有静态数据
不返回指向静态数据的指针;所有数据都由函数的调用者提供
使用本地数据
或者通过制作全局数据的本地拷贝来保护全局数据
}
此函数若被多个进程调用的话
其结果可能是未知的
因为当(**)语句刚执行完后
另外一个使用本函数的进程可能正好被激活
那么当新激活的进程执行到此函数时
将使Exam赋与另一个不同的para值
所以当控制重新回到"temp = Square_Exam( )"后
计算出的temp很可能不是预想中的结果
也就是只要输入数据相同就应产生同样的输出
说明:带有内部"存储器"的函数的功能可能是不可预测的
因为它的输出可能取决于内部存储器(如某标记)的状态
这样的函数既不易于理解又不利于测试和维护
在C/C++语言中
函数的static局部变量是函数的内部存储器
有可能使函数的功能不可预测
然而
当某函数的返回值为指针类型时
此函数应如下改进
unsigned int example( int para )
{
unsigned int temp;
[申请信号量操作] // 若申请不到"信号量"
说明另外的进程正处于
Exam = para; // 给Exam赋值并计算其平方过程中(即正在使用此
for (count = 0; count < num; count++)
{
*sum += data[count]; // sum成了工作变量
不太好
}
}
若改为如下
则更好些
void sum_data( unsigned int num, int *data, int *sum )
尽量只使用数据参数
说明:本建议目的是防止函数间的控制耦合
调度函数是指根据输入的消息类型或控制命令
来启动相应的功能实体(即函数或过程)
而本身并不完成具体功能
控制参数是指改变函数功能行为的参数
即函数要根据此参数来决定具体怎样工作
非调度函数的控制参数增加了函数间的控制耦合
很可能使函数间的耦合度增大
temp = Square_Exam( ); // 信号)
本进程必须等待其释放信号后
才可继
[释放信号量操作] // 续执行
若申请到信号
则可继续执行
但其
// 它进程必须等待本进程释放信号量后
才能再使
自己收集整理的,仅供参考交流,如有错误,请指正!谢谢!
一、可重入函数
1)什么是可重入性?
可重入(reentrant)函数可以由多于一个任务并发使用
而不必担心数据错误
相反
不可重入(non-reentrant)函数不能由超过一个任务所共享
除非能确保函数的互斥(或者使用信号量
}
else
{
return (a b);
}
}
不如分为如下两个函数清晰
int add( int a, int b )
{
return (a + b);
}
int sub( int a, int b )
{
return (a b);
18 :使用动宾词组为执行某操作的函数命名
如果是OOP 方法
可以只有动词(名词是对象本身)
示例:参照如下方式命名函数
void print_record( unsigned int rec_ind ) ;
int input_record( void ) ;
unsigned char get_current_color( void ) ;
亦可方便维护、测试
示例:如下语句的功能不很明显
value = ( a > b ) ? a : b ;
相关文档
最新文档