可重入函数与不可重入函数介绍

不可重入函数的定义:

  • 在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果有一个函数不幸被设计成为这样:那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。这样的函数是不安全的函数,也叫不可重入函数

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

禁用中断原因:不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。我们知道中断时确实保存一些上下文,但是仅限于返回地址,cpu 寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer 等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。

可重入函数的定义:

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

  • 可重入函数可以在任意时刻被中断, 稍后再继续运行,不会丢失数据。可重入函数要么使用本地变量,要么在使用全局变量时 保护自己的数据。

  • 可重入函数可以允许有该函数的多个副本在运行,由于不同任务使用的是分离的栈,所以不会互相干扰。

  • 它除了使用自己栈上的变量以外不依赖于任何环境(包括 static)

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

  1. 避免使用全局或静态变量,尽量使用局部变量
    全局或静态变量可能被多个线程共享,如果不加控制地访问它们,就会导致不可重入。所以在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量);

  2. 避免使用非线程安全的函数库
    某些函数库可能使用全局或静态变量来存储一些数据,这些数据可能会被多个线程共享。如果这些函数库不是线程安全的,也会导致不可重入。

  3. 避免使用死锁
    在设计不可重入函数时,需要避免出现死锁的情况,即两个或多个线程彼此等待对方释放某些资源的情况。

  4. 使用信号量、互斥量等同步机制或采取关中断
    为了保证不可重入函数的线程安全,我们可以使用信号量、互斥量等同步机制来控制对共享变量或资源的访问。

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

  1. 函数体内使用了静态的数据结构;

  2. 函数体内调用了malloc() 或者 free() 函数;

    • malloc就是一个不可重入函数,如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
    • 可重入函数与不可重入函数介绍_第1张图片
  3. 函数体内调用了标准 I/O 函数。因为标准I/O库很多实现都以不可重入的方式使用全局数据结构。

  4. 进行了浮点运算,许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。

  5. 调用printf。

不可重入函数的案例

函数有static变量或者全局变量,则该函数是不可重入函数

#include 
#include 
#include 
#include 
#include 

int g_mysign = 0;
//这个函数会修改全局变量g_mysign的值
void muNEfunc(int value) 
{
    //.....其他处理代码
    g_mysign = value;  
    //.....其他处理代码
}

//信号处理函数
void sig_usr(int signo)
{     

    muNEfunc(22); //因为一些实际需求必须要在sig_user这个信号处理函数里调用muNEfunc

    int myerrno = errno;

    if(signo == SIGUSR1)
    {
        printf("收到了SIGUSR1信号!\n");
    }
    else if(signo == SIGUSR2)
    {
        printf("收到了SIGUSR2信号!\n");
    }
    else
    {
        printf("收到了未捕捉的信号%d!\n",signo);
    }
}

int main(int argc, char *const *argv)
{
    if(signal(SIGUSR1,sig_usr) == SIG_ERR)  //系统函数,参数1:是个信号,参数2:是个函数指针,代表一个针对该信号的捕捉处理函数
    {
        printf("无法捕捉SIGUSR1信号!\n");
    }
    if(signal(SIGUSR2,sig_usr) == SIG_ERR) 
    {
        printf("无法捕捉SIGUSR2信号!\n");
    }
    for(;;)
    {
        sleep(1); //休息1秒
        printf("休息1秒\n");        
        
        /*我们希望在调用muNEfunc函数以后,得到g_mysign的值为15,
        但是,如果在执行完muNEfunc函数以后,突然来了一个信号,这时
        程序执行流程到了信号处理函数当中,而信号处理函数又对muNEfunc
        当中的g_mysign变量的值做出了改变,所以,当信号处理函数执行完毕以后
        再次回到主函数当中时,g_mysign的值便不是我们所希望的了*/
        muNEfunc(15);//调用函数
        printf("g_mysign=%d\n",g_mysign); 
        //拿g_mysign做一些其他用途;
    }
    printf("再见!\n");
    return 0;
}

malloc,free,printf均是不可重入函数(意味不能在中断函数或信号处理函数同时调用)

#include 
#include  
#include 
#include 

//信号处理函数
void sig_usr(int signo)
{   
    //这里也malloc,这是错用,不可重入函数不能用在信号处理函数中;
    int* p;
    p = (int *) malloc (sizeof(int)); //用了不可重入函数;
    free(p);

    if(signo == SIGUSR1)
    {
        printf("收到了SIGUSR1信号!\n");
    }
    else if(signo == SIGUSR2)
    {
        printf("收到了SIGUSR2信号!\n");
    }
    else
    {
        printf("收到了未捕捉的信号%d!\n",signo);
    }
    
}

int main(int argc, char *const *argv)
{
    /*系统函数,参数1:是个信号,参数2:是个函数指针,代表一个针对该信号的捕捉处理函数*/
    if(signal(SIGUSR1,sig_usr) == SIG_ERR)  
    {
        printf("无法捕捉SIGUSR1信号!\n");
    }
    if(signal(SIGUSR2,sig_usr) == SIG_ERR) 
    {
        printf("无法捕捉SIGUSR2信号!\n");
    }
    for(;;)
    {             
        int* p;
        p = (int *) malloc (sizeof(int));
        free(p);
    }
    printf("再见!\n");
    return 0;
}

你可能感兴趣的:(java,开发语言)