Man*_*lva 13 c linux linux-kernel
对于单线程程序,我想检查给定的虚拟地址是否在进程的堆栈中.我想在用C编写的过程中做到这一点.
我正在考虑读取/proc/self/maps标记为[stack]的行来获取进程堆栈的起始和结束地址.考虑这个解决方案,我想到了以下问题:
/proc/self/maps显示我的特定进程的132k堆栈,堆栈的最大大小(ulimit -s)在我的系统上是8兆.Linux如何知道由于我们高于堆栈限制而发生的给定页面错误属于堆栈(并且堆栈必须变大)而不是我们到达进程的另一个内存区域?
Linux会缩小堆栈吗?换句话说,例如,当从深层函数调用返回时,OS是否会减少与堆栈相对应的虚拟内存区域?
操作系统最初为堆栈分配了多少虚拟空间?
我的解决方案是否正确,还有其他更清洁的方法吗?
很多堆栈设置细节取决于您运行的架构,可执行格式和各种内核配置选项(堆栈指针随机化,i386的4GB地址空间等).
在执行该过程时,内核选择默认堆栈顶部(例如,在传统的i386 arch上它是0xc0000000,即虚拟地址空间的用户模式区域的末尾).
可执行格式的类型(ELF vs a.out等)理论上可以改变初始堆栈顶部.然后完成任何额外的堆栈随机化和任何其他修正(例如,当使用时,vdso [系统调用跳板]区域通常放在这里).现在你有一个实际的初始堆栈顶部.
内核现在为进程分配构造参数和环境向量等所需的空间,初始化堆栈指针,创建初始寄存器值,并启动进程.我相信这为(3)提供了答案:即内核仅分配足够的空间来包含参数和环境向量,其他页面按需分配.
其他答案,尽我所知:
(1)当进程尝试将数据存储在堆栈区域当前底部下方的区域中时,会生成页面错误.内核错误处理程序确定进程的虚拟地址空间中下一个填充的虚拟内存区域的开始位置.然后它会查看哪种类型的区域.如果这是一个"向下增长"区域(至少在x86上,所有的堆叠区域应标明增长下降),并在故障时如果进程的堆栈指针(ESP/RSP)值小于底部该区域,如果进程未超过ulimit -s设置,并且该区域的新大小不会与另一个区域发生冲突,那么它被认为是增加堆栈的有效尝试,并且分配了额外的页面以满足这个过程.
(2)不是100%肯定,但我认为没有任何缩小堆栈区域的尝试.假设正常的LRU页面扫描将被执行,使得现在未使用的区域候选者用于寻呼到交换区域,如果它们实际上没有被重新使用的话.
(4)你的计划对我来说似乎是合理的:/ proc/NN/maps应该得到整个堆栈区域的起始和结束地址.我认为这将是你的筹码量最大的筹码.当前实际工作堆栈区域OTOH应该位于当前堆栈指针和区域末尾之间(通常没有任何东西应该使用堆栈指针下方的堆栈区域).
我的答案仅适用于 x64 上的 linux,内核为 3.12.23。它可能适用于也可能不适用于其他版本或架构。
(1)+(2) 我在这里不确定,但我相信正如吉尔·汉密尔顿之前所说的那样。
(3) 您可以在 /proc/pid/maps(或 /proc/self/maps,如果您的目标是调用进程)中看到数量。然而,并非所有这些都可以实际用作您的应用程序的堆栈。参数- (argv[]) 和环境向量 (__environ[]) 通常在该区域的底部(最高地址)消耗相当多的空间。
要实际找到内核为您的应用程序指定为“堆栈”的区域,您可以查看 /proc/self/stat。其值记录在此处。如您所见,有一个“startstack”字段。连同映射区域的大小,您可以计算当前保留的堆栈量。连同“kstkesp”,您可以确定可用堆栈空间或实际使用的堆栈空间的数量(请记住,您的线程完成的任何操作很可能会更改这些值)。
另请注意,这仅适用于进程主线程!其他线程不会得到标记为“[stack]”的映射,但要么使用匿名映射,要么甚至可能最终出现在堆上。(使用 pthreads API 查找这些值,或者记住线程主函数中的堆栈启动)。
(4) 如 (3) 中所述,您的解决方案大多可以,但并不完全准确。
| 归档时间: |
|
| 查看次数: |
4928 次 |
| 最近记录: |