转载自https://mlog.club/article/1807704

在unix系统中,我们知道malloc()是一个不可重入的函数(系统调用)。为什么?
类似地,printf()也被认为是不可重入的;为什么?
我知道重新进入的定义,但我想知道为什么它适用于这些功能。
是什么阻止了它们被保证可重入?

最佳答案:

malloc和printf通常使用全局结构,并在内部使用基于锁的同步。这就是为什么它们不可重入。
malloc函数可以是线程安全的,也可以是线程不安全的。两者都不可重入:
malloc在全局堆上操作,同时发生的两个不同的malloc调用可能返回相同的内存块。(第二个malloc调用应该在获取块的地址之前发生,但块没有标记为不可用)。这违反了malloc的后置条件,因此此实现不会重新进入。
为了防止这种影响,线程安全的malloc实现将使用基于锁的同步。但是,如果从信号处理程序调用malloc,则可能发生以下情况:

malloc();            //initial call
  lock(memory_lock); //acquire lock inside malloc implementation
signal_handler();    //interrupt and process signal
malloc();            //call malloc() inside signal handler
  lock(memory_lock); //try to acquire lock in malloc implementation
  // DEADLOCK!  We wait for release of memory_lock, but 
  // it won't be released because the original malloc call is interrupted

当从不同线程调用malloc时,这种情况不会发生。实际上,可重入性概念超出了线程安全的范畴,还要求函数正常工作,即使其中一个调用从未终止。这就是为什么任何带有锁的函数都不会重新进入的原因。
printf函数也对全局数据进行操作。任何输出流通常都使用一个附加到资源数据的全局缓冲区(用于终端或文件的缓冲区)。打印过程通常是将数据复制到缓冲区,然后刷新缓冲区的序列。这个缓冲区应该像malloc那样被锁保护。因此,printf也是不可重入的。