对于单线程程序,我想检查给定的虚拟地址是否在进程的堆栈中。我想在用 C 编写的进程中执行此操作。
我正在考虑读书/proc/self/maps
找到标记为 [stack] 的行,以获取进程堆栈的开始和结束地址。思考这个解决方案让我产生了以下问题:
/proc/self/maps
显示了我的特定进程的 132k 堆栈,并且堆栈的最大大小 (ulimit -s) 在我的系统上为 8 兆。 Linux 如何知道由于超出堆栈限制而发生的给定页面错误属于堆栈(并且堆栈必须更大),而不是我们正在到达进程的另一个内存区域?
Linux 是否收缩堆栈?换句话说,当从深度函数调用返回时,操作系统是否会减少与堆栈对应的虚拟内存区域?
操作系统最初为堆栈分配了多少虚拟空间?
我的解决方案正确吗?还有其他更清洁的方法吗?
许多堆栈设置细节取决于您运行的体系结构、可执行格式和各种内核配置选项(堆栈指针随机化、i386 的 4GB 地址空间等)。
当进程被执行时,内核选择一个默认的栈顶(例如,在传统的 i386 架构上,它是 0xc0000000,即虚拟地址空间的用户模式区域的末尾)。
可执行格式的类型(ELF 与 a.out 等)理论上可以改变初始堆栈顶部。然后完成任何额外的堆栈随机化和任何其他修复(例如,vdso [系统调用跳板]区域通常在使用时放在这里)。现在你有了一个实际的初始堆栈顶部。
内核现在分配为进程构造参数和环境向量等所需的任何空间,初始化堆栈指针,创建初始寄存器值,并启动进程。我相信这提供了(3)的答案:即内核仅分配足够的空间来包含参数和环境向量,其他页面根据需要分配。
据我所知,其他答案:
(1) 当进程试图将数据存储到当前堆栈区域底部以下的区域时,就会产生页错误。内核故障处理程序确定进程虚拟地址空间中下一个填充的虚拟内存区域的开始位置。然后它会查看该区域是什么类型。如果它是一个“向下增长”区域(至少在 x86 上,所有堆栈区域都应标记为向下增长),并且如果发生故障时进程的堆栈指针 (ESP/RSP) 值小于该区域,如果进程没有超过 ulimit -s 设置,并且该区域的新大小不会与另一个区域冲突,那么它被认为是增长堆栈的有效尝试,并且分配额外的页面以满足的过程。
(2) 不是 100% 确定,但我认为没有任何尝试缩小堆栈区域。大概会执行正常的 LRU 页面扫描,使现在未使用的区域成为调出到交换区域的候选区域(如果它们确实没有被重新使用)。
(4) 你的计划对我来说似乎是合理的: /proc/NN/maps 应该获得整个堆栈区域的起始和结束地址。我认为这将是您有史以来最大的筹码。当前实际工作堆栈区域 OTOH 应位于您的当前堆栈指针和区域的末尾(通常没有任何东西应该使用堆栈的区域below堆栈指针)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)