我正在使用库编写一个多线程 C 程序pthread
。程序malloc()
从线程和主线程调用。当我检查返回的内存地址时malloc()
,主线程中返回的地址malloc()
是通常的地址(如0x80874a0),这是程序的堆部分的地址。但是malloc()
另一个线程(由 创建)返回的地址pthread_create()
有点不寻常,地址位于地址空间的高位(0x7ffa87000a60 看起来有点像堆栈部分中的地址)。分析该线程的某个堆栈变量的地址后,看起来返回的地址位于malloc()
该线程的堆栈附近。
我很困惑,因为据我所知堆程序的一部分由该程序中的所有线程共享,因此任何调用都malloc()
应该返回靠近在一起的地址。它应该返回从一个地址开始的地址。但可以肯定的是事实并非如此。
答案1
根据它的定义,您从 malloc 获得的是“堆”。所以,所有这些都是堆。
GNU libc 的malloc
文档将自己描述为man malloc
在“arena”中执行此操作 ( ):
为了可扩展地处理多线程应用程序中的内存分配,如果检测到互斥锁争用,glibc 会创建额外的内存分配区域。每个 arena 都是一个大的内存区域,由系统内部分配(使用
brk(2)
或mmap(2)
),并使用自己的互斥体进行管理。
我很困惑,因为据我所知,程序的堆部分由该程序中的所有线程共享,
“堆”通常不是我最喜欢的术语。它合并了不同的概念:
- (堆是一种数据结构。内核中的内存管理数据结构和/或由
malloc
堆创建的内存管理数据结构是否是堆都不是给定的(通常不是。)因此,这已经是一个混乱的根源。) - 堆在具有内存保护和虚拟地址的操作系统中,指的是进程空间的某个区域,从中
malloc
取出一块,并在内部将其标记为正在使用(无论是在单独的表中,堆中,...,还是在进程开始之前的标头中)本身)。因此,这是一个含糊不清的术语。您可以brk
根据需要扩展进程虚拟内存,而无需实际占用物理内存。因此,您可以为自己获得额外的 10 GB 空间,然后使用两个不同的起始地址(间隔 5 GB)来分配两个不同的内存区域。这是一个堆,两个区域吗?两个区域,每个区域都有自己的堆?文学界对此并不认同。如果只是用 扩展虚拟内存空间的末尾brk
,我决定mmap
在两个地方的某个地方取消支持内存,该怎么办?那么这是两堆吗?
所以,它实际上可以归结为“堆”的含义你的书,以及您的具体malloc
实现如何实际保留内存。
答案2
对于“大”分配(与页面大小相比较大),GNU C 库的实现malloc
用途mmap
。这可能就是您在这里看到的 - 第一个分配是使用 的malloc
arenas 处理的,第二个分配是使用mmap
(使用更高的地址)处理的。
看内核中进程内存测量更新的速度/频率如何?来说明这种行为。