嵌入式c语言学习笔记:可重入函数与不可重入函数

什么是可重入函数与不可重入函数?

在一个多任务环境中,一个函数如果可以被多次重复调用,或者被多个任务并发调用,函数在运行过程中可以随时随地被打断,并不影响该函数的运行结果,我们称这样的函数为可重入函数。相反,如果一个函数不能多次并发调用,在执行过程中不能被中断,否则就会影响函数的运行结果,那么这个函数就是不可重入函数。如何判断一个函数是可重入函数,还是不可重入函数呢?规则很简单,一个函数如果满足下列条件中的任何一个,那么这个函数就是不可重入函数。

  • 函数内部使用了全局变量或者局部静态变量;
  • 函数返回值是一个全局变量或者静态变量;
  • 函数调用了标准I/O函数;
  • 函数体内调用了 malloc() 或者 free() 函数;
  • 函数内调用了其他不可重入函数;
    看如下例子:
int a[10]={1,2,3,4,5,6,7,8,9,0};
int b[20]={1,2,3,4,5,6,7,8,9,0,11,12,13,14,9,8};
int sum(int array[],int len)
{
	static int sum=0;
	for(int i=0;i<len;i++)
	{
	  sum+=i;
	}
	return sum;
}
void task1(void)
{
   sum(a,10);
}
void task2(void)
{
   sum(b,20);
}

假设在一个多任务环境中,我们定义了一个sum()函数用来对数组累加求和。sum()函数首先被任务task1调用,在sum()函数运行期间,任务task1被调度器挂起,CPU切换到task2运行,sum()函数被任务task2再次调用。由于sum()函数内定义的有静态变量,当在运行期间
被打断后再次被调用,就有可能影响sum()函数在task1和task2中的运行结果。

在裸机环境下面,我们不需要考虑函数的可重入问题,因为裸机环境下只有一个主程序main()一直在独占CPU运行。但是在多任务环境下,如果该函数可能被多次调用,或者在执行过程中可能会被中断或被任务调度器打断,此时我们就要考虑该函数的可重入问题了。

为什么中断处理函数不能直接调用不可重入函数?

在多任务系统下,中断可能在任何情况下发生,如果一个函数在执行期间被中断,到它重新恢复到断点处继续执行,函数所依赖的上下文环境没有发生改变,那么这个函数就是可重入的。
在中断发生前后,会保存和恢复上下文,怎么会出现函数所依赖的环境发生改变了呢? 我们知道中断时确实保存一些上下文,但是仅限于返回地址,cpu 寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer 等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。
在中断处理函数中调用有互斥锁保护的全局变量,如果恰好该变量正在被另一个线程调用,会导致中断处理函数不能及时返回,导致中断丢失等严重问题。

如何重写不可重入函数?

  • 函数体内不使用全局变量;
  • 如果必须使用全局变量,记住利用互斥信号量来保护全局变量。或者调用函数前关中断,调用后开中断;
  • 不使用静态局部变量;
  • 坚持只使用缺省态(auto)局部变量;
  • 在和硬件发生交互的时候,切记关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/退出核心”或者用 OS_ENTER_KERNAL/OS_EXIT_KERNAL 来描述;
  • 在中断函数中不能调用任何不可重入的函数;
  • 谨慎使用堆栈。最好先在使用前先 OS_ENTER_KERNAL;

你可能感兴趣的:(嵌入式c语言高级编程,c语言,学习,笔记)