`
weiyuhu
  • 浏览: 231545 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

可重入函数与不可重入函数

阅读更多
原文地址:http://blog.chinaunix.net/u/25994/showart_369466.html
可重入函数与不可重入函数

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

也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。

编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。

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



示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。那么如下函数不具有可重入性。

unsigned int example( int para )

{

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

unsigned int example( int para ) {
unsigned int temp;
[申请信号量操作] //(1)
Exam = para;
temp = Square_Exam( );
[释放信号量操作]
return temp;
}
(1)若申请不到“信号量”,说明另外的进程正处于给Exam赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。

保证函数的可重入性的方法:
在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(如采取关中断、信号量等方法),这样构成的函数就一定是一个可重入的函数。
VxWorks中采取的可重入的技术有:
* 动态堆栈变量(各子函数有自己独立的堆栈空间)
* 受保护的全局变量和静态变量
* 任务变量


--------------------------------------------------
在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。那么什么是可重入函数呢?所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。不可重入函数在实时系统设计中被视为不安全函数。满足下列条件的函数多数是不可重入的:
1) 函数体内使用了静态的数据结构;
2) 函数体内调用了malloc()或者free()函数;
3) 函数体内调用了标准I/O函数。

下面举例加以说明。
A. 可重入函数
void strcpy(char *lpszDest, char *lpszSrc)

{
while(*lpszDest++=*lpszSrc++);
*dest=0;
}

B. 不可重入函数1
charcTemp;//全局变量
void SwapChar1(char *lpcX, char *lpcY)

{
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//访问了全局变量
}

C. 不可重入函数2
void SwapChar2(char *lpcX,char *lpcY)

{
static char cTemp;//静态局部变量
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//使用了静态局部变量
}

问题1,如何编写可重入的函数?
答:在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。如果必须访问全局变量,记住利用互斥信号量来保护全局变量。

问题2,如何将一个不可重入的函数改写成可重入的函数?
答:把一个不可重入函数变成可重入的唯一方法是用可重入规则来重写它。其实很简单,只要遵守了几条很容易理解的规则,那么写出来的函数就是可重入的。
1) 不要使用全局变量。因为别的代码很可能覆盖这些变量值。
2) 在和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”。
3) 不能调用其它任何不可重入的函数。
4) 谨慎使用堆栈。最好先在使用前先OS_ENTER_KERNAL。

堆栈操作涉及内存分配,稍不留神就会造成益出导致覆盖其他任务的数据,所以,请谨慎使用堆栈!最好别用!很多黑客程序就利用了这一点以便系统执行非法代码从而轻松获得系统控制权。还有一些规则,总之,时刻记住一句话:保证中断是安全的!

实例问题:曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?
unsigned int sum_int( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意,是static类型
for (index = 1; index <= base; index++)
sum += index;
return sum;
}

分析:所谓的函数是可重入的(也可以说是可预测的),即只要输入数据相同就应产生相同的输出。这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。因此如果需要一个可重入的函数,一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。
将上面的函数修改为可重入的函数,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto类型的变量,函数即变为一个可重入的函数。
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
分享到:
评论

相关推荐

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

    主要介绍了Linux 中可重入函数与不可重入函数详解的相关资料,需要的朋友可以参考下

    总结:重入函数和不可重入函数

    主要用于多任务环境中,一个可重入的函数简单来说就是可以被...而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

    可重入详解可重入详解

    若一个程序或子程序可以安全的被并行执行,则称其为可重入(reentrant或re-entrant)的;即,当该子程序正在运行时,可以再次进入并执行它。若一个函数是可重入的,则该函数: ...不能调用不可重入的函数。

    Golang教程之不可重入函数的实现方法

    主要给大家介绍了关于Golang教程之不可重入函数的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    关于不可微函数的局部分数阶多重积分

    关于不可微函数的局部分数阶多重积分,杨小军,,根据分形理论的特点,在分形空间上考虑的函数具有不可微的性质。局部分数阶微积分学是解决不可微函数的理论。本文是在局部分数阶

    ORACLE四舍五入函数

    经常为四舍五入的方法烦恼,新的思路重写四舍五入,并可以根据小数位数自动格式化,适合于数值型表格显示,该思路可适用于任何系统或数据库,代码比较乱,欢迎指正。

    多任务系统中的不可重函数使用注意事项

    多任务系统中的不可重函数使用注意事项

    处处连续处处不可导函数

    处处连续处处不可导函数的构造 曾经轰动一时的发现

    华大MCU延时函数重定义校准

    官方库延时函数误差太大,近2倍,在此基于嘀嗒...2、例程中“time.c" 创建了 系统节拍模型,可用来实现 某些函数 每间隔一定时间运行1次 的功能。可实现 【时间片轮】模型的 单片机系统结构,执行效率高,避免阻塞延时

    解析函数的概念但是,函数在一点处解析与在一点处可导是不等价的概念. 即函数在一点处可导, 不一定在该点处解析.

    但是,函数在一点处解析与在一点处可导是不等价的概念. 即函数在一点处可导, 不一定在该点处解析. 但是,函数在一点处解析与在一点处可导是不等价的概念. 即函数在一点处可导, 不一定在该点处解析. 但是,函数在一点处...

    PHP函数库,PHP函数大全,PHP函数实例,PHP函数手册,PHP5函数库实例

    PHP函数库,PHP函数大全,PHP函数实例,PHP函数手册,PHP5函数库实例 PHP函数库,PHP函数大全,PHP函数实例,PHP函数手册,PHP5函数库实例 curl获取远程文件内容 GD显示中文 PHP GIF动画生成类 PHP HTML转UBB函数 ...

    【Linux】— 线程安全 VS可重入

    线程安全 VS 可重入什么是线程安全和可重入常见的线程不安全的情况(重点)常见的线程安全的情况(重点)常见的可重入情况常见不可重入的情况可重入与线程安全联系可重入与线程安全区别(重点) 什么是线程安全和可重入 ...

    特殊函数计算手册

    由特定微分方程的解定义的特殊函数有正交多项式(如Chebyshev、Laguerre和Hermite多项式),Gamma函数,Legendre函数类,Bessel函数(如球Bessel、变型Bessel、Ricatti-Bessel函数等),Kelvin函数,Airy函数,...

    Excel函数词典(483个函数说明).xlsx

    Excel用得好不好,函数用得溜不溜,是重要的判断标准之一。从简单的加减乘除四则运算,再到复杂的财务税收计算、工程计算或是数学计算,都用得到函数。Excel中,内置了财务、逻辑、文本、日期、时间、查找、引用、...

    强物理不可克隆函数的侧信道混合攻击_刘威.pdf

    强物理不可克隆函数的侧信道混合攻击_刘威强物理不可克隆函数的侧信道混合攻击_刘威强物理不可克隆函数的侧信道混合攻击_刘威强物理不可克隆函数的侧信道混合攻击_刘威

    Matlab中使用varargin来实现参数可变的函数

    在写《用Matlab演示Parzen窗法》的时候,我还不知道怎样在m中设计可变参数函数。 后来阅读一些m自带函数源码时发现了它的秘密,只要将最后一个参数设置为

    论文研究 - Cantor函数的不可微子集的维

    本说明的主要目的是估计点集Tμλ的大小,在这些点上Cantor函数是不可微的,我们发现Tμλ的Hausdorff维数为[log2 / log3] 2。 另外,Tμλ的包装尺寸为log2 / log3。 log2 = loge2是,如果ax = N(a&gt; 0,且a≠1),...

    07_ReLU函数在0处不可导,为什么还能用1

    问题背景这是在阿的机器学习岗的时候问的个问题,最开始的问题是“为什么机器学习中解决回归问题的时候般使平损失(即均误差)?当时我的回答是损失函数是是模型预测值与真

Global site tag (gtag.js) - Google Analytics