1. 可重入函数和不可重入函数的概念
- 可重入函数:在函数的执行过程中,可以被打断并重新进入自身,或可以自己调用自己(用于递归处理)的函数;
- 不可重入函数:在函数的执行过程中,不可以被打断并重新进入自身,也不可自己调用自己。
2. 可重入函数编写的注意事项
- 简单来讲,在一般的体系架构中,函数中只要不使用全局资源(如全局变量),便可认为这个函数是可重入的。
理由是,局部变量一般会保存到栈中或者寄存器中,每个函数都有自己的栈帧,寄存器中的局部变量,编译器也会保证在不同函数中是独立的。所以即使函数重入了,他们所访问的同一局部变量也是存储在不同的存储空间中。
3. 8051编译出不可重入函数的原因简析
8051即使不访问全局资源,编译出的函数也可能是不可重入的,理由如下:
- 简单来讲,8051受资源限制,编译器并不会为每个局部变量创建栈帧,实际上可能依旧放在全局数据段,本质上是全局的(“局部”特性仅在编译阶段起作用,运行时可被破坏)。
4. 函数重入的原因
- 多线程(多核),(8051一般不会有多核的处理器)
- 递归调用。
- 中断调用
4. 8051 编程中的注意事项
4.1 8051识别有重入风险的方法
- 编译器警告
- 人工识别
4.2 递归调用产生函数重入风险的的示例及编译器打印信息
void test (void)
{
volatile int tmp[10];
tmp[0]++;
test();
}
void main (void)
{
test();
while(1);
}
4.3 中断调用产生函数重入风险的的示例及编译器打印信息
void test (void)
{
volatile int tmp[10];
tmp[0]++;
}
void INT0_IRQHandler (void) interrupt 0
{
test();
}
void main (void)
{
test();
while(1);
}
4.4 8051不可重入函数重入的解决办法
- 关于递归调用,只能由人工判断和解决其风险,尽量避免递归。
- 不考虑多核的情况下,可通过关中断的方式保护函数在执行过程中不被打断。
4.5 8051编译器无法识别出重入风险的一种情况分析
- 在使用函数指针的情况下,编译器是无法识别出函数重入的风险的,如下示例,在中断中使用函数指针代替函数调用,编译器就无法发现函数的重入风险了:
typedef void (*func_t) (void); void test (void) { volatile int tmp[10]; tmp[0]++; } func_t p_test = test; void INT0_IRQHandler (void) interrupt 0 { p_test(); } void main (void) { test(); while(1); }
注:编译器也仅仅是在编译期将可能的风险作为警告提示用户,而且可能存在有些风险发现不了的情况,这也对编程人员提出了更高的要求,但8051程序体量往往比较小,也是比较容易把控的,程序员在进行8051编程时尽量不要写有重入风险的程序。