8051编译器生成不可重入函数的原因简析与使用的注意事项

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);
}

8051编译器生成不可重入函数的原因简析与使用的注意事项_第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);
}

8051编译器生成不可重入函数的原因简析与使用的注意事项_第2张图片

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编程时尽量不要写有重入风险的程序。

你可能感兴趣的:(c语言,汇编,开发语言)