如何以编程方式获取Linux进程的堆栈开始和结束地址? [英] How programmatically get Linux process's stack start and end address?

查看:59
本文介绍了如何以编程方式获取Linux进程的堆栈开始和结束地址?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于单线程程序,我想检查给定的虚拟地址是否在进程的堆栈中.我想在用C编写的过程中做到这一点.

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.

我正在考虑阅读/proc/self/maps 来找到标有[stack]的行,以获取进程堆栈的开始和结束地址.考虑这种解决方案后,我想到了以下问题:

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. Thinking about this solution led me to the following questions:

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

  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. 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会收缩堆栈吗?换句话说,例如,当从深度函数调用返回时,操作系统是否会减少与堆栈相对应的虚拟内存区域?

Does Linux shrink back the stack ? In other words, when returning from deep function calls for example, does the OS reduce the virtual memory area corresponding to the stack ?

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

How much virtual space is initially allocated for the stack by the OS ?

我的解决方案正确吗,还有其他更清洁的方法吗?

Is my solution correct and is there any other cleaner way to do that ?

推荐答案

很多堆栈设置详细信息取决于您所运行的体系结构,可执行格式以及各种内核配置选项(堆栈指针随机化,4GB地址空间用于i386等).

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拱形上,它是0xc0000000,即虚拟地址空间的用户模式区域的末尾).

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, i.e. the end of the user-mode area of the virtual address space).

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

The type of executable format (ELF vs a.out, etc) can in theory change the initial stack top. 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). Now you have an actual initial top of stack.

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

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): i.e. the kernel allocates only enough space to contain the argument and environment vectors, other pages are allocated on demand.

其他答案,据我所知:

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

(1) When a process attempts to store data in the area below the current bottom of the stack region, a page fault is generated. 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.

(2)不确定100%,但是我不认为有任何缩小堆栈面积的尝试.如果确实不被重用,那么大概会执行正常的LRU页面扫描,从而使现在未使用的区域候选者可以调出到交换区域.

(2) Not 100% sure, but I don't think there's any attempt to shrink stack areas. 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.

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

(4) Your plan seems reasonable to me: the /proc/NN/maps should get start and end addresses for the stack region as a whole. 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).

这篇关于如何以编程方式获取Linux进程的堆栈开始和结束地址?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆