简体   繁体   English

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

[英]How programmatically get Linux process's stack start and end address?

For a mono threaded program, I want to check whether or not a given virtual address is in the process's stack. 对于单线程程序,我想检查给定的虚拟地址是否在进程的堆栈中。 I want to do that inside the process which is written in C. 我想在用C编写的过程中做到这一点。

I am thinking of reading /proc/self/maps to find the line labelled [stack] to get start and end address for my process's stack. 我正在考虑读取/proc/self/maps以找到标记为[stack]的行来获取进程堆栈的起始和结束地址。 Thinking about this solution led me to the following questions: 考虑这个解决方案,我想到了以下问题:

  1. /proc/self/maps shows a stack of 132k for my particular process and the maximum size for the stack (ulimit -s) is 8 mega on my system. /proc/self/maps显示我的特定进程的132k堆栈,堆栈的最大大小(ulimit -s)在我的系统上是8兆。 How does Linux know that a given page fault occurring because we are above the stack limit belongs to the stack (and that the stack must be made larger) rather than that we are reaching another memory area of the process ? Linux如何知道由于我们高于堆栈限制而发生的给定页面错误属于堆栈(并且堆栈必须变大)而不是我们到达进程的另一个内存区域?

  2. Does Linux shrink back the stack ? Linux会缩小堆栈吗? In other words, when returning from deep function calls for example, does the OS reduce the virtual memory area corresponding to the stack ? 换句话说,例如,当从深层函数调用返回时,OS是否会减少与堆栈相对应的虚拟内存区域?

  3. How much virtual space is initially allocated for the stack by the OS ? 操作系统最初为堆栈分配了多少虚拟空间?

  4. Is my solution correct and is there any other cleaner way to do that ? 我的解决方案是否正确,还有其他更清洁的方法吗?

Lots of the stack setup details depend on which architecture you're running on, executable format, and various kernel configuration options (stack pointer randomization, 4GB address space for i386, etc). 很多堆栈设置细节取决于您运行的架构,可执行格式和各种内核配置选项(堆栈指针随机化,i386的4GB地址空间等)。

At the time the process is exec'd, the kernel picks a default stack top (for example, on the traditional i386 arch it's 0xc0000000, ie the end of the user-mode area of the virtual address space). 在执行该过程时,内核选择默认堆栈顶部(例如,在传统的i386 arch上它是0xc0000000,即虚拟地址空间的用户模式区域的末尾)。

The type of executable format (ELF vs a.out, etc) can in theory change the initial stack top. 可执行格式的类型(ELF vs a.out等)理论上可以改变初始堆栈顶部。 Any additional stack randomization and any other fixups are then done (for example, the vdso [system call springboard] area generally is put here, when used). 然后完成任何额外的堆栈随机化和任何其他修正(例如,当使用时,vdso [系统调用跳板]区域通常放在这里)。 Now you have an actual initial top of stack. 现在你有一个实际的初始堆栈顶部。

The kernel now allocates whatever space is needed to construct argument and environment vectors and so forth for the process, initializes the stack pointer, creates initial register values, and initiates the process. 内核现在为进程分配构造参数和环境向量等所需的空间,初始化堆栈指针,创建初始寄存器值,并启动进程。 I believe this provides the answer for (3): ie the kernel allocates only enough space to contain the argument and environment vectors, other pages are allocated on demand. 我相信这为(3)提供了答案:即内核仅分配足够的空间来包含参数和环境向量,其他页面按需分配。

Other answers, as best as I can tell: 其他答案,尽我所知:

(1) When a process attempts to store data in the area below the current bottom of the stack region, a page fault is generated. (1)当进程尝试将数据存储在堆栈区域当前底部下方的区域中时,会生成页面错误。 The kernel fault handler determines where the next populated virtual memory region within the process' virtual address space begins. 内核错误处理程序确定进程的虚拟地址空间中下一个填充的虚拟内存区域的开始位置。 It then looks at what type of area that is. 然后它会查看哪种类型的区域。 If it's a "grows down" area (at least on x86, all stack regions should be marked grows-down), and if the process' stack pointer (ESP/RSP) value at the time of the fault is less than the bottom of that region and if the process hasn't exceeded the ulimit -s setting, and the new size of the region wouldn't collide with another region, then it's assumed to be a valid attempt to grow the stack and additional pages are allocated to satisfy the process. 如果它是一个“增长”区域(至少在x86上,所有堆栈区域应标记为增长),并且如果进程的堆栈指针(ESP / RSP)值在故障时小于底部该区域,如果进程没有超过ulimit -s设置,并且该区域的新大小不会与另一个区域发生冲突,那么它被认为是增加堆栈的有效尝试,并且分配了额外的页面来满足这个过程。

(2) Not 100% sure, but I don't think there's any attempt to shrink stack areas. (2)不是100%肯定,但我认为没有任何缩小堆栈区域的尝试。 Presumably normal LRU page sweeping would be performed making now-unused areas candidates for paging out to the swap area if they're really not being re-used. 假设正常的LRU页面扫描将被执行,使得现在未使用的区域候选者用于寻呼到交换区域,如果它们实际上没有被重新使用的话。

(4) Your plan seems reasonable to me: the /proc/NN/maps should get start and end addresses for the stack region as a whole. (4)你的计划对我来说似乎是合理的:/ proc / NN / maps应该得到整个堆栈区域的起始和结束地址。 This would be the largest your stack has ever been, I think. 我认为这将是你的筹码量最大的筹码。 The current actual working stack area OTOH should reside between your current stack pointer and the end of the region (ordinarily nothing should be using the area of the stack below the stack pointer). 当前实际工作堆栈区域OTOH应该位于当前堆栈指针和区域末尾之间(通常没有任何东西应该使用堆栈指针下方的堆栈区域)。

My answer is for linux on x64 with kernel 3.12.23 only. 我的答案是针对x64上的linux,只有内核3.12.23。 It might or might not apply to aother versions or architectures. 它可能适用于或不适用于其他版本或体系结构。

(1)+(2) I'm not sure here, but I believe it is as Gil Hamilton said before. (1)+(2)我不确定这里,但我相信这是吉尔汉密尔顿之前所说的。

(3) You can see the amount in /proc/pid/maps (or /proc/self/maps if you target the calling process). (3)您可以在/ proc / pid / maps中查看金额(如果您定位调用流程,则可以查看/ proc / self / maps)。 However not all of that it actually useable as stack for your application. 然而,并非所有这些实际上都可用作应用程序的堆栈。 Argument- (argv[]) and environment vectors (__environ[]) usually consume quite a bit of space at the bottom (highest address) of that area. 参数 - (argv [])和环境向量(__environ [])通常在该区域的底部(最高地址)消耗相当多的空间。

To actually find the area the kernel designated as "stack" for your application, you can have a look at /proc/self/stat. 要实际找到内核为您的应用程序指定为“堆栈”的区域,您可以查看/ proc / self / stat。 Its values are documented here . 其价值记录在这里 As you can see, there is a field for "startstack". 如您所见,有一个“startstack”字段。 Together with the size of the mapped area, you can compute the current amount of stack reserved. 与映射区域的大小一起,您可以计算当前保留的堆栈数量。 Along with "kstkesp", you could determine the amount of free stack space or actually used stack space (keep in mind that any operation done by your thread most likely will change those values). 与“kstkesp”一起,您可以确定可用堆栈空间的数量或实际使用的堆栈空间(请记住,您的线程执行的任何操作很可能会更改这些值)。

Also note, that this works only for the processes main thread! 另请注意,这仅适用于进程主线程! Other threads won't get a labled "[stack]" mapping, but either use anonymous mappings or might even end up on the heap. 其他线程不会得到标记的“[stack]”映射,但要么使用匿名映射,要么甚至可能最终在堆上。 (Use pthreads API to find those values, or remember the stack-start in the threads main function). (使用pthreads API查找这些值,或者记住threads main函数中的stack-start)。

(4) As explained in (3), you solution is mostly OK, but not entirely accurate. (4)如(3)中所述,您的解决方案大多没问题,但并不完全准确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM