c - 如何以编程方式获取 Linux 进程的堆栈起始地址和结束地址?

标签 c linux linux-kernel

对于单线程程序,我想检查给定的虚拟地址是否在进程的堆栈中。我想在用 C 编写的进程中执行此操作。

我正在考虑阅读 /proc/self/maps 以找到标记为 [stack] 的行以获取我的进程堆栈的开始和结束地址。思考这个解决方案让我想到了以下问题:

  1. /proc/self/maps 显示了我的特定进程的 132k 堆栈,堆栈的最大大小 (ulimit -s) 在我的系统上是 8 兆。 Linux 如何知道由于超出堆栈限制而发生的给定页面错误属于堆栈(并且堆栈必须变大)而不是我们正在到达进程的另一个内存区域?

    <
  2. Linux 会收缩堆栈吗?换句话说,例如从深度函数调用返回时,操作系统是否减少了堆栈对应的虚拟内存区域?

  3. 操作系统最初为堆栈分配了多少虚拟空间?

  4. 我的解决方案是否正确,还有其他更简洁的方法吗?

最佳答案

许多堆栈设置细节取决于您运行的架构、可执行格式和各种内核配置选项(堆栈指针随机化、i386 的 4GB 地址空间等)。

在执行进程时,内核会选择一个默认的栈顶(例如,在传统的 i386 arch 上它是 0xc0000000,即虚拟地址空间的用户模式区域的末尾)。

理论上,可执行格式的类型(ELF 与 a.out 等)可以改变初始栈顶。然后完成任何额外的堆栈随机化和任何其他修复(例如,vdso [系统调用跳板]区域通常在使用时放在这里)。现在您有了一个实际的初始堆栈顶部。

内核现在为进程分配构建参数和环境 vector 等所需的空间,初始化堆栈指针,创建初始寄存器值,并启动进程。我相信这为 (3) 提供了答案:即内核仅分配足够的空间来包含参数和环境 vector ,其他页面按需分配。

其他答案,据我所知:

(1) 当一个进程试图在栈区当前底部以下的区域中存储数据时,会产生页面错误。内核故障处理程序确定进程虚拟地址空间中下一个填充的虚拟内存区域的开始位置。然后它会查看那是什么类型的区域。如果它是一个“向下增长”区域(至少在 x86 上,所有堆栈区域都应该被标记为向下增长),并且如果在故障发生时进程的堆栈指针(ESP/RSP)值小于底部该区域,并且如果进程没有超过 ulimit -s 设置,并且该区域的新大小不会与另一个区域冲突,那么它被认为是增加堆栈的有效尝试,并且分配额外的页面以满足过程。

(2) 不是 100% 确定,但我不认为有任何缩小堆栈区域的尝试。大概会执行正常的 LRU 页面扫描,使现在未使用的区域成为候选区域,如果它们真的没有被重新使用的话,可以将它们调出到交换区域。

(4) 你的计划对我来说似乎是合理的:/proc/NN/maps 应该得到整个堆栈区域的开始和结束地址。我认为,这将是您有史以来最大的堆栈。当前实际工作的堆栈区域 OTOH 应该位于您的当前堆栈指针和区域末尾之间(通常没有任何东西应该使用堆栈指针下方的堆栈区域) .

关于c - 如何以编程方式获取 Linux 进程的堆栈起始地址和结束地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23243532/

相关文章:

c - 如何在 C 中操作一般输出(不是数字或变量)?

c - 声明内联函数后,什么会再次导致多重定义错误?

c - 如何控制特定进程的 CPU 使用率?

linux - 线程池中的段错误

linux - 未找到 yocto defconfig

c - 有没有办法修改专有内核模块中的 Linux 系统调用表?

C不兼容指针错误

c - 将 ZMQ 上下文从 C 传递到嵌入式 Lua

java - Cassandra 2.0 及更高版本需要 Java 7u25 或更高版本,但我使用的是 8u60

linux-kernel - 在 ARM 中禁用内存页的写保护