malloc线程安全吗?

时间:2009-05-13 02:17:42

标签: c multithreading thread-safety malloc

malloc()函数是否可重入?

12 个答案:

答案 0 :(得分:95)

问题:“是malloc reentrant”吗? 答:不,不是。以下是对例程reentrant的定义的一个定义。

malloc的常见版本都不允许您重新输入它(例如,来自信号处理程序)。请注意,可重入例程可能不使用锁,并且现有的几乎所有malloc版本都使用锁(这使它们成为线程安全的)或全局/静态变量(这使得它们不是线程不安全的非-reentrant)。

到目前为止,所有的答案都回答“是malloc线程安全吗?”,这是一个完全不同的问题。要那个提问,答案是,它取决于在你的运行时库上,可能还有你使用的编译器标志。在任何现代UNIX上,默认情况下您将获得一个线程安全的malloc。在Windows上,使用/MT/MTd/MD/MDd标志来获取线程安全的运行时库。

答案 1 :(得分:40)

我在某处读到如果使用-pthread进行编译,则malloc变为线程安全。我很确定它的实现依赖,因为malloc是ANSI C而线程不是。

如果我们正在谈论gcc:

  

编译并链接-pthread和   malloc()在x86上是线程安全的   和AMD64。

http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2431a99b9bdcef11/ea800579e40f7fa4

另一种观点,更有洞察力

  

{malloc,calloc,realloc,免费,   glibc-2.2 +的posix_memalign}   线程安全

http://linux.derkeiler.com/Newsgroups/comp.os.linux.development.apps/2005-07/0323.html

答案 2 :(得分:9)

以下是来自glibc的malloc.c的摘录:

线程安全:除非定义了NO_THREADS,否则是线程安全的

假设默认情况下没有定义NO_THREADS,malloc至少在linux上是线程安全的。

答案 3 :(得分:8)

这是一个非常古老的问题,我希望根据当前的状况带来新鲜感。

是的,目前malloc()是线程安全的。

来自glibc-2.20 [released 2014-09-07]的{​​{3}}:

  

void * malloc (size_t size)

     

初步:MT-Safe | ...

     

...   1.2.2.1 POSIX安全概念:

     

... MT-Safe或线程安全功能在现场呼叫是安全的   其他线程在MT-Safe中,MT代表Multi Thread。

     

MT-Safe并不意味着函数是原子的,也不是它使用的   POSIX向用户公开的任何内存同步机制。   甚至可能按顺序调用MT-Safe功能   不会产生MT-Safe组合。例如,进行线程调用   两个MT-Safe功能一个接一个不保证   行为相当于两者结合的原子执行   函数,因为其他线程中的并发调用可能会干扰a   破坏性的方式。

     

可以在库中内联函数的整个程序优化   接口可能会暴露不安全的重新排序,因此执行内联   建议不要使用GNU C Library接口。记录在案   整体计划优化不保证MT-安全状态。   但是,用户可见标头中定义的功能设计为   内联安全。

答案 4 :(得分:5)

是的,在POSIX.1-2008 malloc下是线程安全的。

  

2.9.1线程安全

     

本卷POSIX.1-2008定义的所有函数都是线程安全的,但以下函数1不需要是线程安全的。

     

[不包含malloc的函数列表]

答案 5 :(得分:4)

如果您正在使用GLIBC,答案是:是,但是。

具体来说,是的,但是,请注意,虽然malloc和free是线程安全的,但调试功能不是。

具体来说,非常有用的mtrace(),mcheck()和mprobe()函数不是线程安全的。在GNU项目中你会看到的最简短,最直接的答案之一,这里有解释:

https://sourceware.org/bugzilla/show_bug.cgi?id=9939

您需要考虑其他技术,例如ElectricFence,valgrind,dmalloc等。

所以,如果你的意思是,"是malloc()和free()函数线程安全",答案是肯定的。但如果你的意思是,"是整个malloc / free 套件线程安全",答案是否定的。

答案 6 :(得分:1)

这取决于您正在使用的C运行时库的实现。例如,如果您正在使用MSVC,则可以使用编译器选项来指定要构建的库的哪个版本(即,通过保护或不支持多线程的运行时库)。 / p>

答案 7 :(得分:1)

不,它不是线程安全的。您的C库中实际上可能有malloc_lock()malloc_unlock()函数。我知道这些存在于Newlib库中。我不得不使用它为我的处理器实现一个互斥锁,它在硬件中是多线程的。

答案 8 :(得分:1)

malloc和free不可重入,因为它们使用静态数据结构来记录哪些内存块是空闲的。因此,没有分配或释放内存的库函数是可重入的。

答案 9 :(得分:0)

答案 10 :(得分:0)

简短的回答:是的,从C11(包含线程概念的C标准的第一个版本)开始,malloc和朋友必须是线程安全的。许多同时包含线程和C运行时的操作系统都早于C标准就做出了保证,但是我不准备发誓 all 。但是,malloc和朋友不是,也永远不需要重新进入。

这意味着,可以安全地从多个线程中同时调用mallocfree,而不必担心锁定,只要您不违反任何其他内存分配规则即可(例如对free返回的每个指针仅调用一次malloc。但是从信号处理程序中调用这些函数是不安全的,该信号处理程序可能已经中断了处理信号的线程中对mallocfree的调用。有时,使用ISO C以外的功能,您可以保证处理信号的线程不会中断对mallocfree的调用,例如与sigprocmasksigpause一起使用,但是除非没有其他选择,否则请不要这样做,因为很难完全正确。


引文长的答案:C标准在2011 revision中添加了线程的概念(链接到文档N1570,它是与2011年正式文本最接近的近似文本,该文本可免费免费获得)。在该修订版中,section 7.1.4 paragraph 5指出:

  

除非在下面的详细描述中另有明确说明,否则库函数应防止数据争用,如下所述:库函数不得直接或间接访问除当前线程以外的线程可访问的对象,除非通过这些对象直接或间接访问函数的参数。库函数不得直接或间接修改可由当前线程以外的线程访问的对象,除非通过函数的非常量参数直接或间接访问这些对象。如果对象对用户不可见并且可以防止数据竞争,则实现可以在线程之间共享它们自己的内部对象。

     

[脚注:这意味着,例如,不允许实现在不进行同步的情况下将静态对象用于内部目的,因为即使在没有显式共享线程之间的对象的程序中,它也可能导致数据争用。同样,不允许memcpy实现复制超出目标对象指定长度的字节,然后恢复原始值,因为如果程序在线程之间共享这些字节,则可能导致数据争用。]

据我所知,这是一种冗长的说法,它要求C标准定义的库函数必须是线程安全的(通常意义上:您可以同时从多个线程中调用它们,而无需这样做任何锁定自己的对象,只要它们最终不会与作为参数传递的数据发生冲突),除非特定功能的文档明确指出并非如此。

然后,7.22.3p2确认malloc,calloc,realloc,aligned_alloc和free特别是线程安全的:

  

为了确定是否存在数据争用,内存分配函数的行为就好像它们仅访问可通过其参数访问的存储位置,而不访问其他静态持续时间存储一样。但是,这些功能可能会明显地修改它们分配或取消分配的存储。释放内存区域p的对free或realloc的调用与分配全部或部分区域p的任何分配调用同步。这种同步发生在分配函数对p的任何访问之后,以及分配函数对p的任何访问之前。

7.24.5.8p6中对比一下关于strtok的说法,这不是而且从来都不是线程安全的:

  

不需要strtok函数来避免数据与strtok函数的其他调用争用。

     

[脚注:可以使用strtok_s函数来避免数据争用。]

(脚注上的注释:请勿使用strtok_s,请使用strsep。)

较旧的C标准版本对线程安全性一无所知。但是,他们 did 说了一些关于可重入的问题,因为信号一直是C标准的一部分。这就是他们所说的,可以追溯到原始的1989 ANSI C standard(此文档的措词与来年的ISO C标准几乎完全相同,但章节编号却大不相同):

  

如果发生[a]信号而不是调用中止的结果   或raise函数,如果信号处理程序的行为未定义   调用标准库中除信号以外的任何函数   功能本身或引用具有静态存储持续时间的任何对象   除了通过为静态存储持续时间变量分配值之外   类型为volatile sig_atomic_t。此外,如果这样   信号函数导致SIG_ERR返回,errno的值为   不确定。

这是长篇大论的说法,一般而言, 不需要C库函数。 C11 7.14.1.1p5中仍然出现非常相似的措辞:

  

如果[a]信号不是通过调用abort或raise函数的结果发生的,则如果信号处理程序引用具有静态或线程存储持续时间的任何对象,而该对象不是无锁原子对象,则该行为未定义而不是通过为声明为volatile sig_atomic_t的对象分配值,或者信号处理程序调用标准库中的任何函数,而不是中止函数,_Exit函数,quick_exit函数或第一个参数等于信号的信号函数与导致处理程序调用的信号相对应的数字。此外,如果对信号函数的此类调用导致SIG_ERR返回,则errno的值不确定。

     

[脚注:如果异步信号处理程序生成了任何信号,则行为是不确定的。]

POSIX要求使用much longer, but still short compared to the overall size of the C library,可从“异步信号处理程序”安全地调用的功能列表,并且还详细定义了信号可能“发生的情况,而不是调用信号的结果”的情况。中止或提高功能。”如果您要对信号做一些微不足道的操作,则可能是在编写要在具有Unix性质的OS上运行的代码(与Windows,MVS或嵌入的,可能没有完整托管C实现的软件相反)首先),那么您应该熟悉POSIX的要求以及ISO C的要求。

答案 11 :(得分:0)

我建议阅读

  

§31.1线程安全(和可重入性)

《 Linux编程接口》一书的

,它解释了线程安全性和可重入性以及malloc之间的区别。

节选:

  

如果可以安全地调用函数,则该函数被称为线程安全的   同时有多个线程;相反,如果一个函数是   不是线程安全的,那么我们不能从一个线程中调用它   在另一个线程中执行。

     

....
   此函数说明了一个函数不起作用的典型原因   线程安全:它使用所有线程共享的全局或静态变量。

     

...
  尽管使用关键部分来实现线程安全非常重要   改进了每功能互斥锁的使用,但效率仍然较低   因为锁定和解锁互斥锁会产生成本。重入函数   无需使用互斥体即可实现线程安全。通过避免使用来做到这一点   全局和静态变量。

     

...
  但是,并非所有功能都可以   成为可重入的。通常的原因如下:

     
      
  • 从本质上讲,某些功能必须访问全局数据结构。 malloc库中的函数提供了一个很好的示例。这些功能保持了   堆上空闲块的全局链接列表。 malloc库的功能   通过使用互斥锁使线程安全。
  •   
     

....

绝对值得一读。

为回答您的问题,malloc是线程安全的,但不可重入。