可重入函数与不可重入函数概念以及编写规范

1、定义

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

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

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

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

2、案例

void swap1(int* x, int* y) {
    tmp=*x;
    *x=*y;
    *y=tmp;
}

void swap2(int* x, int* y) {
    int tmp;
    tmp=*x;
    *x=*y;
    *y=tmp;
}

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

假如我们引入了全局变量到重入函数中比如下面,这个案例

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;
    }

倘若上述中Square_Exam( ) 依旧是一个重入函数,在执行过程中,继续调用 example 这个函数的时候(函数递归),但是由于由于主函数没有立即释放掉信号量,此时就容易造成死锁(http://blog.csdn.net/xy010902100449/article/details/45896055)。

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

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

VxWorks中采取的可重入的技术有:

* 动态堆栈变量(各子函数有自己独立的堆栈空间)

* 受保护的全局变量和静态变量

* 任务变量

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

3、不可重入怎么改改成可重入函数

在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。那么什么是可重入函数呢?所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。不可重入函数在实时系统设计中被视为不安全函数。满足下列条件的函数多数是不可重入的:

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类型,则返回为错指针。

时间: 2024-10-26 17:06:21

可重入函数与不可重入函数概念以及编写规范的相关文章

linux: 可重入函数与不可重入函数

参考: 1. 摘自 多线程和多进程的区别(小结) http://blog.csdn.net/hairetz/article/details/4281931 要确保函数线程安全,主要需要考虑的是线程之间的共享变量. 属于同一进程的不同线程会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器. 因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量.局部静态变量.分配于堆的变量都是共享的.在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式. 要

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

重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰.,常见的情况是,程序执行到某个函数foo()时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数foo(),这样便发生了所谓的重入.此时如果foo()能够正确的运行,而且处理完

操作系统--可重入函数和不可重入函数

可重入函数主要用于多任务环境中,简单来说就是可以被中断的函数,即在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,返回控制时不会出现什么错误:也意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是 purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰.而不可重入的函数由于使用了一些系统资源,比如全局变量区.中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下

UNIX高级环境编程(13)信号 - 概念、signal函数、可重入函数

信号就是软中断. 信号提供了异步处理事件的一种方式.例如,用户在终端按下结束进程键,使一个进程提前终止. ? 1 信号的概念 每一个信号都有一个名字,它们的名字都以SIG打头.例如,每当进程调用了abort函数时,都会产生一个SIGABRT信号. 每一个信号对应一个正整数,定义在头文件<signal.h>中. 没有信号对应整数0,kill函数使用信号编号0表示一种特殊情况,所以信号编号0又叫做空信号(null signal). 下面的各种情况会产生一个信号: 当用户在终端按下特定的键时,会产生

浅谈可重入函数与不可重入函数

在实时系统的设计中,经常会出现多个任务调用同一个函数的情况.如果有一个函数不幸被设计成为这样:那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果.这样的函数是不安全的函数,也叫不可重入函数. 相反,肯定有一个安全的函数,这个安全的函数又叫可重入函数.那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错. 一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执

可重入函数与不可重入函数【转】

本文转载自:http://www.cnblogs.com/parrynee/archive/2010/01/29/1659071.html 可重入函数 在 实时系统的设计中,经常会出现多个任务调用同一个函数的情况.如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任 务调用这个函数的数据,从而导致不可预料的后果.那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会 出错.不可重入函数在实时系统设计中被视为不安全函数.

函数可重入性及编写规范

一.可重入函数1)什么是可重入性?可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误.相反, 不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断).可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据.可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据. 2)可重入函数:不为连续的调用持有静态数据. 不返回指向静态数据的指针:所有数据都由函数的调用者提供. 使用本地

C语言可重入函数和不可重入函数

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

可重入函数和不可重入函数

个函数在被调用执行期间(尚未调用结束),由于某种时序又被重复调用,称之为"重入".根据函数实现的方法可分为"可重入函数"和"不可重入函数"两种.看如下时序. 显然,insert函数是不可重入函数,重入调用,会导致意外结果呈现.究其原因,是该函数内部实现使用了全局变量. 注意事项 定义可重入函数,函数内不能含有全局变量及static变量,不能使用malloc.free 信号捕捉函数应设计为可重入函数 信号处理程序可以调用的可重入函数可参阅man 7