使用pmap分析进程的内存映射. [堆] [英] Analyzing memory mapping of a process with pmap. [stack]

查看:129
本文介绍了使用pmap分析进程的内存映射. [堆]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解堆栈在Linux中的工作方式.我阅读了 AMD64 ABI 关于堆栈和进程初始化的章节,目前尚不清楚应该如何映射堆栈.这是相关的报价(3.4.1):

I'm trying to understand how stack works in Linux. I read AMD64 ABI sections about stack and process initialization and it is not clear how the stack should be mapped. Here is the relevant quote (3.4.1):

堆栈状态

本节介绍exec(BA_OS)为之创建的计算机状态 新流程.

This section describes the machine state that exec (BA_OS) creates for new processes.

不确定数据段和堆栈段是否最初是 是否具有执行权限映射.需要的应用程序 在堆栈上执行代码或数据段应采取适当的措施 预防措施,例如通过调用mprotect().

It is unspecified whether the data and stack segments are initially mapped with execute permissions or not. Applications which need to execute code on the stack or data segments should take proper precautions, e.g., by calling mprotect().

因此,我可以从上面的引号推断出堆栈已映射(未指定是否使用PROT_EXEC创建映射).映射也是由exec创建的.

So I can deduce from the quotes above that the stack is mapped (it is unspecified if PROT_EXEC is used to create the mapping). Also the mapping is created by exec.

问题是主线程"的堆栈是使用MAP_GROWSDOWN | MAP_STACK映射还是使用sbrk?

The question is whether the "main thread"'s stack uses MAP_GROWSDOWN | MAP_STACK mapping or maybe even via sbrk?

pmap -x <pid>处,堆栈用[stack]标记为

00007ffc04c78000     132      12      12 rw---   [ stack ]

创建映射为

mmap(NULL, 4096,
     PROT_READ | PROT_WRITE,
     MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK,
     -1, 0);

简单地创建匿名映射,如pmap -x <pid>中所示

simply creates anonymous mapping as that is shown in pmap -x <pid> as

00007fb6e42fa000       4       0       0 rw---   [ anon ]

推荐答案

我可以从上面的引号推断出堆栈已映射

I can deduce from the quotes above that the stack is mapped

从字面上看,这仅意味着已分配内存.即,从这些虚拟地址到物理页面存在逻辑映射.我们之所以知道这一点,是因为您可以在_start中使用pushcall指令,而无需从用户空间进行系统调用来分配堆栈.

That literally just means that memory is allocated. i.e. that there is a logical mapping from those virtual addresses to physical pages. We know this because you can use a push or call instruction in _start without making a system call from user-space to allocate a stack.

实际上,x86-64 System V ABI指定在进程启动时argc,argv和envp在堆栈中.

In fact the x86-64 System V ABI specifies that argc, argv, and envp are on the stack at process startup.

问题是主线程"的堆栈是使用MAP_GROWSDOWN | MAP_STACK映射还是使用sbrk?

ELF二进制加载程序为主线程的堆栈设置_GROWSDOWN标志,但不设置MAP_STACK标志.这是内核内的代码 ,它不会通过常规的mmap系统调用接口.

The ELF binary loader sets the _GROWSDOWN flag for the main thread's stack, but not the MAP_STACK flag. This is code inside the kernel, and it does not go through the regular mmap system call interface.

(用户空间中没有任何东西使用mmap(MAP_GROWSDOWN) ,因此通常,主线程堆栈是内核中唯一具有VM_GROWSDOWN标志的映射.)

用于堆栈的虚拟内存区域(VMA)的标志的内部名称称为VM_GROWSDOWN.如果您有兴趣,这里是用于主线程堆栈的所有标志:VM_GROWSDOWNVM_READVM_WRITEVM_MAYREADVM_MAYWRITEVM_MAYEXEC.另外,如果将ELF二进制文件指定为具有可执行堆栈(例如,通过使用gcc -z execstack进行编译),则还将使用VM_EXEC标志.请注意,在支持向上增长的堆栈的体系结构上,如果内核是使用定义的CONFIG_STACK_GROWSUP编译的,则使用VM_GROWSUP而不是VM_GROWSDOWN.在Linux内核中指定这些标志的代码行可以找到

The internal name of the flag that is used for the virtual memory aree (VMA) of the stack is called VM_GROWSDOWN. In case you're interested, here are all the flags that are used for the main thread's stack: VM_GROWSDOWN, VM_READ, VM_WRITE, VM_MAYREAD, VM_MAYWRITE, and VM_MAYEXEC. In addition, if the ELF binary is specified to have an executable stack (e.g., by compiling with gcc -z execstack), the VM_EXEC flag is also used. Note that on architectures that support stacks that grow upwards, VM_GROWSUP is used instead of VM_GROWSDOWN if the kernel was compiled with CONFIG_STACK_GROWSUP defined. The line of code where these flags are specified in the Linux kernel can be found here.

/proc/.../mapspmap不使用VM_GROWSDOWN,而是依靠地址比较.因此,他们可能无法确切确定主线程堆栈所占用的虚拟地址空间的确切范围(请参阅

/proc/.../maps and pmap don't use the VM_GROWSDOWN - they rely on address comparison instead. Therefore they may not be able to determine exactly the exact range of the virtual address space that the main thread's stack occupies (see an example). On the other hand, /proc/.../smaps looks for the VM_GROWSDOWN flag and marks each memory region that has this flag as gd. (Although it seems to ignore VM_GROWSUP.)

所有这些工具/文件都忽略MAP_STACK标志.实际上,整个Linux内核都会忽略此标志(这可能是程序加载器未设置该标志的原因.)用户空间仅将其传递以用于将来的验证,以防内核 想要启动.专门处理线程堆栈分配.

All of these tools/files ignore the MAP_STACK flag. In fact, the whole Linux kernel ignores this flag (which is probably why the program loader doesn't set it.) User-space only passes it for future-proofing in case the kernel does want to start treating thread-stack allocations specially.

sbrk在这里没有意义;堆栈与中断"不连续,并且brk堆无论如何都朝着堆栈向上增长. Linux将堆栈放在虚拟地址空间的顶部附近.因此,当然不能为主堆栈分配sbrk(相当于内核).

sbrk makes no sense here; the stack isn't contiguous with the "break", and the brk heap grows upward toward the stack anyway. Linux puts the stack very near the top of virtual address space. So of course the primary stack couldn't be allocated with (the in-kernel equivalent of) sbrk.

不,没有任何东西使用MAP_GROWSDOWN ,甚至没有使用辅助线程堆栈,因为通常不能安全地使用它.

And no, nothing uses MAP_GROWSDOWN, not even secondary thread stacks, because it can't in general be used safely.

mmap(2)手册页上说MAP_GROWSDOWN被用于堆栈"是可笑的过时和误导.请参阅如何为堆栈映射在Linux上进行clone()系统调用?.正如Ulrich Drepper 在2008年解释的那样,使用MAP_GROWSDOWN的代码通常被破坏,并建议删除该标志.来自Linux mmap和glibc标头. (这显然没有发生,但是pthreads从那时起就没有使用过它,如果有的话.)

The mmap(2) man page which says MAP_GROWSDOWN is "used for stacks" is laughably out of date and misleading. See How to mmap the stack for the clone() system call on linux?. As Ulrich Drepper explained in 2008, code using MAP_GROWSDOWN is typically broken, and proposed removing the flag from Linux mmap and from glibc headers. (This obviously didn't happen, but pthreads hasn't used it since well before then, if ever.)

MAP_GROWSDOWN为内核内部的映射设置VM_GROWSDOWN标志.主线程使用该标志来启用增长机制,因此线程栈可能能够以与主栈相同的方式增长:任意远(最多可达ulimit -s?)指针在页面错误位置下方. (Linux不需要堆栈探针"即可接触大型多页堆栈阵列或alloca的每一页.)

MAP_GROWSDOWN sets the VM_GROWSDOWN flag for the mapping inside the kernel. The main thread also uses that flag to enable the growth mechanism, so a thread stack may be able to grow the same way the main stack does: arbitrarily far (up to ulimit -s?) if the stack pointer is below the page fault location. (Linux does not require "stack probes" to touch every page of a large multi-page stack array or alloca.)

(线程堆栈是在前面完全分配的;只有正常的物理页面的惰性分配支持该虚拟分配才能避免浪费线程堆栈的空间.)

(Thread stacks are fully allocated up front; only normal lazy allocation of physical pages to back that virtual allocation avoids wasting space for thread stacks.)

MAP_GROWSDOWN映射也可以按照mmap手册页描述的方式进行扩展:访问最低映射页面下方的保护页面"也将触发增长,即使该区域位于红色区域底部也是如此.

MAP_GROWSDOWN mapping can also grow the way the mmap man page describes: access to the "guard page" below the lowest mapped page will also trigger growth, even if that's below the bottom of the red zone.

但是主线程的堆栈具有> 所不具备的魔力.它最多保留ulimit -s的增长空间,以防止随机选择mmap解决从创建障碍到堆栈增长的问题.只有在execve()期间映射主线程堆栈的内核程序加载器才能使用此魔术,从而使其免受mmap(NULL, ...)随机阻塞未来堆栈增长的影响.

But the main thread's stack has magic you don't get with mmap(MAP_GROWSDOWN). It reserves the growth space up to ulimit -s to prevent random choice of mmap address from creating a roadblock to stack growth. That magic is only available to the in-kernel program-loader which maps the main thread's stack during execve(), making it safe from an mmap(NULL, ...) randomly blocking future stack growth.

mmap(MAP_FIXED)仍可以为主堆栈创建包版,但如果使用MAP_FIXED,则100%负责不破坏任何内容. (无限堆栈不能超出初始堆栈132KiB(如果涉及到MAP_FIXED?). MAP_FIXED将替换现有的映射和​​保留,但是其他任何操作都将把主线程的堆栈增长空间视为保留;. (我认为是正确的;值得尝试使用MAP_FIXED_NOREPLACE或只是一个非NULL的提示地址)

mmap(MAP_FIXED) could still create a roadblock for the main stack, but if you use MAP_FIXED you're 100% responsible for not breaking anything. (Unlimited stack cannot grow beyond the initial 132KiB if MAP_FIXED involved?). MAP_FIXED will replace existing mappings and reservations, but anything else will treat the main thread's stack-growth space as reserved;. (I think that's true; worth trying with MAP_FIXED_NOREPLACE or just a non-NULL hint address)

请参见

pthread_create不会将MAP_GROWSDOWN用于线程堆栈,其他任何人也不应使用.通常不使用.默认情况下,Linux pthreads为线程堆栈分配完整大小.这会占用虚拟地址空间,但是(直到被实际触摸)不会占用物理页面.

pthread_create doesn't use MAP_GROWSDOWN for thread stacks, and neither should anyone else. Generally do not use. Linux pthreads by default allocates the full size for a thread stack. This costs virtual address space but (until it's actually touched) not physical pages.

不一致的结果导致对的评论为什么MAP_GROWSDOWN映射不增长(有人发现它起作用,有些人在触摸返回值和下面的页面时发现它仍然存在段错误)听起来像

The inconsistent results in comments on Why is MAP_GROWSDOWN mapping does not grow? (some people finding it works, some finding it still segfaults when touching the return value and the page below) sound like https://bugs.centos.org/view.php?id=4767 - MAP_GROWSDOWN may even be buggy outside of the way the standard main-stack VM_GROWSDOWN mapping is used.

这篇关于使用pmap分析进程的内存映射. [堆]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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