可重入函数
一个函数在执行的过程中被打断,然后会再被从头执行一次,执行完后,再回来把刚才没执行完的部分执行完。这就相当于嵌套的执行了。函数是公共代码,这样的执行是允许的。函数的执行可以被打断,打断之后还可以再从头执行,执行完后接着执行刚才没有执行的代码,然后第一次执行的代码(被打断的函数)执行结果还是正确的。也就是说,这个函数执行,无论中间把这个函数再嵌入执行多少遍,怎么嵌入,最终执行完都不会对函数内部功能/程序逻辑造成影响,并且执行的结果都是正确的,这样的函数就是可重入函数。
我们平常所写的函数,常常都是用的函数内部的局部变量,由于是函数内部的局部变量,那么如果出现了函数在执行的过程中被打断,然后被从头执行,执行完毕后,由于我们使用的都是函数内部的局部变量,会释放在函数内部所创建的局部变量等,这样等我们返回到之前被中断的地方去的时候,就不会影响此时这个函数的内部的变量内容,也就是说我们的函数是可重入函数。
常用的可重入函数:
- 不要使用全局变量,防止别的代码覆盖这些变量的值。
- 调用这类函数之前先关掉中断,调用完之后马上打开中断。防止函数执行期间被中断进入别的任务执行。
- 使用信号量(互斥条件)。
总而言之:要保证中断是安全的。
我们平常写的一些在栈上创建的函数,如果此函数被重入,多次重入时,互相之间并不会影响它们的指针指向。所以在重入时是可以的。
不可重入函数
函数执行期间,被中断,从头执行这个函数,执行完毕后再返回刚才的中断点继续执行,此时由于刚才的中断导致了现在从新在中断点执行时发生了不可预料的错误。也就是说,如果函数在不同的地方/时序进行调用,会对函数的功能逻辑造成影响,这种函数就称为不可重入函数。
我们知道,在中断函数之后会将中断点的一些数据保存起来,如上下文数据等,那么这些数据不是被保存了吗?怎么还会被影响?其实我们在保存上下文数据的时候,仅仅保存了一小部分,而且那一小部分也只是地址,而对全局变量、静态变量这些并没有保存。所以一旦中断之后,返回到中断点,此时就是不可预料的了。
常见的不可重入函数:
- 使用了静态数据结构
- 调用了malloc和free等
- 调用了标准I/O函数
- 进行了浮点运算
malloc与free是不可重入的,它们使用了全局变量来指向堆区。标准I/O大多都使用了全局数据结构。
浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现)。
- 一个函数中判断是否是可重入函数:
1.是否对全局性(static)的数据进行修改操作
2.这个操作是否是原子性(不可被打断)的
不可重入函数:对全局性变量的进行修改操作,且这个操作不是原子性的
- 如何避免写出不可重入函数
不使用或者互斥使用全局变量,不使用静态局部变量,只使用局部变量;在函数中动态分配的内存只在本函数中使用,不会传递函数外使用;只要保证局部特性,函数中使用的所有东西都只有局部性,对外不公开,用完即释放,就可以保证可重入。这样,不管怎么重叠,反正本层的函数的东西只有本层能够使用,其他层的函数无法使用,就相当于隔离每一层的变量。这样,不管重叠多少次,都不可能相互影响。这样每一层函数执行的结果都是正确的。