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

来源:互联网 发布:js聚合物水泥防水阴角 编辑:程序博客网 时间:2024/04/30 08:25
可重入函数与不可重入函数(转)

转自:http://www.cppblog.com/franksunny/archive/2007/08/03/29269.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类型,则返回为错指针。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 泰拉瑞亚神庙祭坛挖掉了怎么办 泰拉瑞亚不小心把数据删除了怎么办 足球守门员遇到单刀球怎么办 猫见到主人就跑怎么办 手机玩游戏闪屏怎么办 满身起小疙瘩很痒怎么办 出门在外忘记带备用胸罩怎么办 去泰国浮潜近视怎么办 gta5线上马丁任务卡了怎么办 双肩背包黑色的皮子褪色怎么办 lspdfr在游戏里崩溃怎么办 gta5钱超过21亿怎么办 假警察要带走我怎么办 遇到假警察拦车怎么办 大连船员体检眼力不合格怎么办 禁行如果通过了怎么办 钻戒的戒圈磨损怎么办 多肉的肉掉了怎么办 电脑自带鼠标动不了怎么办 笔记本电脑自带鼠标动不了怎么办 包包弹簧扣松了怎么办 耳钉氧化成黑色怎么办 想买点小股票玩玩要怎么办 玩具子弹打到眼睛怎么办 玩具汽车遥控器码乱了怎么办 索尼A7屏幕花了怎么办? 汽车冷冻器坏了怎么办 sim卡损坏怎么办 补卡 异或门一个输入怎么办 迷你世界加不了好友怎么办 电脑软件显示无效应用程序怎么办 美的滚筒洗衣机打不开门怎么办 手机存储卡坏了怎么办 回南天地板潮湿怎么办 lg滚筒洗衣机门打不开怎么办 西门子滚桶洗衣机门打不开怎么办 洗衣机离合器螺丝卸不动怎么办 门锁保险栓坏了怎么办 小车电瓶没电了怎么办 重装机兵战车底盘坏了怎么办 父亲沉迷安利十年该怎么办