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

今天在看51单片机的时候遇到一个名词:不可重入函数。听起来比较怪,故写一篇记录。

什么是不可重入函数

不可重入函数就是执行过程中不能被中断的函数,该函数在被中断后重新再进入执行可能会发生问题,因此叫不可重入。反之,在这个函数执行的任何时刻可以中断进入其他代码,而返回时不会出现任何错误的函数就是可重入函数。

更加通俗一点,就是这个函数是不能被共享的,同一时间只能有一个任务在使用

什么样的函数不可重入

但一个函数满足一下条件时,是不可重入的:

  • 函数内使用了静态(static)或全局变量
  • 函数内调用了malloc()/free()
  • 函数内调用了标准I/O函数

可见当函数内部使用了全局静态类型的东西时,就是不可重入。

为什么函数不可重入

在多任务系统下,中断可能在任务执行的任何时间发生;如果一个函数的执行期间被中断后,到重新恢复到断点进行执行的过程中,函数所依赖的环境没有发生改变,那么这个函数就是可重入的,否则就不可重入。

而进入中断后,这些全局类型的东西可能在中断期间发生改变,那么当函数回到断点继续执行时,其结果就不可预料了。

如何预防

  • 在函数体内不访问那些全局变量;
  • 如果必须访问全局变量,记住利用互斥信号量来保护全局变量。或者调用该函数前关中断,调用后再开中断;
  • 不使用静态局部变量;
  • 不能调用任何不可重入的函数;

总之就是注意防止运行中静态/全局变量的改变。

在C51中的体现

现在回到上面的问题。在C51中,由于没有硬件栈(?),为了提高程序的运行效率,所有局部变量存储在内存的固定位置(就像加了static),因此所有函数都是不可重入函数。为了实现可重入,需要用一个模拟栈(运行效率低)来储存局部变量,因此需要使用reentrant来标出需要重入的函数

1
2
3
4
int fun(char a,char b) reentrant//这样就是可重入的了
{
return a+b;
}