不可重入函数的定义:
禁用中断原因:不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。我们知道中断时确实保存一些上下文,但是仅限于返回地址,cpu 寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer 等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。
可重入函数的定义:
-
这个安全的函数又叫可重入函数,所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。
-
可重入函数可以在任意时刻被中断
, 稍后再继续运行,不会丢失数据。可重入函数要么使用本地变量,要么在使用全局变量时 保护自己的数据。
-
可重入函数可以允许有该函数的多个副本在运行,由于不同任务使用的是分离的栈,所以不会互相干扰。
-
它除了使用自己栈上的变量以外不依赖于任何环境(包括 static)
保证函数的可重入性的方法:
-
避免使用全局或静态变量,尽量使用局部变量
全局或静态变量可能被多个线程共享,如果不加控制地访问它们,就会导致不可重入。所以在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量);
-
避免使用非线程安全的函数库
某些函数库可能使用全局或静态变量来存储一些数据,这些数据可能会被多个线程共享。如果这些函数库不是线程安全的,也会导致不可重入。
-
避免使用死锁
在设计不可重入函数时,需要避免出现死锁的情况,即两个或多个线程彼此等待对方释放某些资源的情况。
-
使用信号量、互斥量等同步机制或采取关中断
为了保证不可重入函数的线程安全,我们可以使用信号量、互斥量等同步机制来控制对共享变量或资源的访问。
满足下列条件的函数多数是不可重入(不安全)的:
-
函数体内使用了静态的数据结构;
-
函数体内调用了malloc() 或者 free() 函数;
- malloc就是一个不可重入函数,如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
-
函数体内调用了标准 I/O 函数。因为标准I/O库很多实现都以不可重入的方式使用全局数据结构。
-
进行了浮点运算,许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。
-
调用printf。
不可重入函数的案例
函数有static变量或者全局变量,则该函数是不可重入函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
//信号处理函数
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;
}