1、可重入函数

1)举例说明:

wKiom1eFt_ODO2E7AADHBhucysc467.png-wh_50

main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后向链表中插入两个节点,而最后只有一个节点真正插入链表中了。


insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入

insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数

反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant)函数


2)可重入性(reentrant)针对函数,它有两个方面的内涵:

(1)可并行/并发,同时进入:指可重入函数被某任务调用时,其它任务可同时进行调用而不产生错误的结果;或称在相同的输入情况下可重入函数的执行所产生的效果,并不因其并发的调用而产生不同,也称并发安全


(2)中断后可重新进入:指可重入函数可被任意的中断,当中断执行完毕,返回断点可以继续正确的运行下去;或称在相同的输入情况下可重入函数的执行所产生的结果,并不因为在函数执行期间有中断的调用而产生不同,也称中断安全


2、不可重入函数的条件

如果函数满足以下条件,则属于不可重入函数:

1)调用了malloc或free。(因为malloc也是用全局链表来管理堆的)

2)调用了标准I/O库函数。(标准I/O库的很多实现都以不可重入的方式使用全局数据结构)

3)众所周知它们使用了静态数据结构体


3、可重入函数的条件

1)不为连续的调用持有静态数据。

2)不返回指向静态数据的指针;所有数据都由函数的调用者提供。

3)使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。

4)绝不调用任何不可重入函数。


4、线程安全

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。

若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。


5、两者的区别与联系

可重入和线程安全是两个不同的概念:可重入函数一定是线程安全的;线程安全的函数可能是重入的,也可能是不重入的;线程不安全的函数一定是不可重入的。


可重入 => 线程安全

可重入函数要解决的问题是,不在函数内部使用静态或全局数据,不返回静态或全局数据,也不调用不可重入函数。
线程安全函数要解决的问题是,多个线程调用函数时访问资源冲突。