可重入函数与不可重入函数
概念
- 可重入函数(reentrant):在任意时刻可以被中断,稍后再继续运行,不会丢失数据,即不用担心数据被其他线程篡改。可重入函数必须满足以下三个条件:
- 可以在执行的过程中可以被打断;
- 被打断之后,在该函数一次调用执行完之前,可以再次被调用(或进入,reentered)。
- 再次调用执行完之后,被打断的上次调用可以继续恢复执行,并正确执行。
- 不可重入函数(non-reentrant):多任务调用某一不可重入函数时,可能修改其他任务调用该函数的数据,从而导致不可预料的后果。即不可重入函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。满足下面条件之一的都数是不可重入函数:
- 使用了静态数据结构或者全局变量;
- 调用了malloc或free;
- 调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。
- 进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。
- 调用printf。( 注:printf引用全局stdout; malloc, free会引用全局的内存分配表。)
如何保证可重入
- 不要使用全局变量和static变量。因为别的代码很可能改变这些变量值。
- 在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量);
- 对于要使用的全局变量要加以保护(如采取关中断、信号量等互斥方法),这样构成的函数就一定是一个可重入的函数。
- 不能调用其它任何不可重入的函数。
- 谨慎使用堆栈。
- 在和硬件发生交互的时候,切记执行类似 disinterrupt() 之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做“进入/ 退出核心”。
可重入与线程安全
- 可重入函数与线程安全没有必然联系。即,一个可重入函数不一定是线程安全的,一个线程安全的函数不一定是可重入的。比如printf,是线程安全的,但是却不可重入。
- 但,不可重入函数一定不是线程安全的。
代码示例
- 不可重入且线程不安全
- 使用全局变量,导致线程不安全。
int t;
void swap(int *x, int *y)
{
t = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = t;
}mianain
void main()
{
int x = 1, y = 2;
swap(&x, &y);
return;
}
- 可重入但线程不安全
- 在swap函数里,在交换前,对此时刻的t全局变量做一个本地的缓存,在交换结束的时候,始终使用该缓存。这样的话,swap函数在退出的时候,全局变量的之跟进入的时候是一样的。这样,就可以保证该函数是线程可重入的。
- 在多线程环境下,依然是线程不安全的,因为它无法保证全局变量t的一致性。
int t;
void swap(int *x, int *y)
{
int s;
s = t; // save global variable</span>
t = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = t;
t = s; // restore global variable</span>
}
void main()
{
int x = 1, y = 2;
swap(&x, &y);
return;
}
Linux常见可重入函数
参考
https://blog.csdn.net/acs713/article/details/20034511
May 29, 2020 AM 10:28 @DaYang