堆地址范围内全局变量的地址

Posted

技术标签:

【中文标题】堆地址范围内全局变量的地址【英文标题】:Address of a Global Variable in the Heap Address Range 【发布时间】:2019-06-01 07:48:39 【问题描述】:

我正在调试MPlayer-1.3.0 源代码,我看到一个全局变量,其地址(由GDB 甚至简单的打印返回)在堆分配范围内,而不是数据部分。我使用procfs 检查了堆范围。

555555554000-555555834000 r-xp 00000000 08:12 798876  /usr/bin/mplayer
555555a33000-555555b25000 r--p 002df000 08:12 798876  /usr/bin/mplayer
555555b25000-555555b2b000 rw-p 003d1000 08:12 798876  /usr/bin/mplayer
555555b2b000-555556479000 rw-p 00000000 00:00 0       [heap]
7fffc3fff000-7fffc8000000 rw-s 00000000 00:16 1932    /dev/shm/pulse-shm-3887887751

变量定义为int verbose = 0;,位于mp_msg.cline 40,地址为0x555555b3bbb0,在[heap]映射中。我什至检查了它之前和之后的一些变量定义:

int mp_msg_levels[MSGT_MAX]; // verbose level of this module. initialized to -2
int mp_msg_level_all = MSGL_STATUS;
int verbose = 0;
int mp_msg_color = 0;
int mp_msg_module = 0;

其中,只有mp_msg_level_all 位于数据部分。任何帮助表示赞赏。

【问题讨论】:

【参考方案1】:

假设您的问题是“为什么根据/proc/self/maps 分配给[heap] 内存映射的int verbose = 0;?”,答案是这样的

    整个[heap] 概念实际上是早已被遗忘的过去的遗物,并且 传统的[heap] 紧跟在.bss 之后,它们通常共享相同的映射,所以这里没什么值得惊讶。

稍微扩展一点,在旧的传统 UNIX 内存模型中(在线程和 mmap 成为事物之前),在堆栈向下增长的处理器上,内存的上半部分保留给内核空间,堆栈从用户内存的最高端开始,程序.text本身从地址0开始,紧随其后的是.data.bss,然后是堆(brk/sbrk种类)。这允许堆增长到更高的地址,并为组合堆+堆栈提供最大可用内存。

在存在线程、共享库和内存映射文件的情况下,该模型根本无法正常工作,并且在很大程度上已被现代 malloc 实现所抛弃,这些实现几乎不会打扰 sbrk。相反,他们只是 mmap 他们需要的内存(并且任何此类内存都不会显示在 [heap] 中,您在 procfs 中看到)。

附言

将零页映射到进程空间的想法早已被放弃,因为它只会导致错误。这就是为什么.text 在所有现代 UNIXen 上都从更高的地址开始。 给内核一半的可用地址空间也是相当浪费的,32 位 Linux 开始给内核提供更少的空间。在 64 位系统上,地址空间不足不再是问题。

更新:

所以你的意思是 [heap] 包含 .bss 和堆的一部分。那么,确定地址是否在堆内的唯一方法是跟踪 malloc(),free(),... 调用?

我觉得我解释得不好。

在进程空间中有一个称为“堆”的区域的概念已经过时。现代的malloc 实现可能有多个线程特定的arenas,通过mmap 从系统获得,堆分配的对象可以在其中任何一个中。

你不能轻易地说“哦,这个地址 0x568901234 看起来像堆”,因为它可以是任何东西。

如果 procfs 输出已过时,在 Linux 中确定进程的虚拟内存区域(例如 .text、heap 和 .bss)的地址范围的标准方法是什么?

在这里,您再次尝试用有些过时的术语来解释内存布局:在大多数进程中没有.text.bss,因为每个共享库都会有它自己的(除了主要的可执行文件)。还有许多附加部分(.tls.plt.got 等)而且部分甚至都不是必需的 em> 在运行时——ELF(在运行时)只需要段,而不关心段。

【讨论】:

感谢@used-russian。所以你的意思是[heap] 包含.bssheap 的一部分。那么,确定地址是否在堆内的唯一方法是跟踪malloc(),free(),... 调用?我说的对吗? One more question: 确定Linux 中进程的虚拟内存区域(例如.textheap.bss)的地址范围的标准方法是什么,如果@ 987654355@ 输出已过时? 经验相对于普通知识的好处在你的回答中很明显,这表明你不是昨天从萝卜车上摔下来的:) 再次感谢。你知道在[heap]对应的范围内只捕获动态数据访问(不是.bss访问)的方法吗?【参考方案2】:

mp_msg_level_all 位于数据部分中,因为它已初始化为非零值。其余部分初始化为 0,因此属于 bss 部分。

为什么链接器决定将bss 放入我们可能想知道的明显堆地址范围内。

【讨论】:

以上是关于堆地址范围内全局变量的地址的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript中全局变量和局部变量的不同

程序内存地址的分配

全局变量与静态变量

检查变量是不是设置在全局范围内?

进程的虚拟地址空间,堆栈堆数据段代码段

内存分配