macOS如何为进程分配堆栈和堆? [英] How does macOS allocate stack and heap for a process?

查看:154
本文介绍了macOS如何为进程分配堆栈和堆?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道macOS如何为进程分配堆栈和堆内存,即macOS中进程的内存布局.我只知道将mach-o可执行文件的段加载到页面中,但是找不到与进程的堆栈或堆区域相对应的段.有关于这个的文件吗?

I want to know how macOS allocate stack and heap memory for a process, i.e. the memory layout of a process in macOS. I only know that the segments of a mach-o executable are loaded into pages, but I can't find a segment that correspond to stack or heap area of a process. Is there any document about that?

推荐答案

让我们退后一步-Mach-o格式描述了将二进制段映射到虚拟内存的方法.重要的是,您提到的内存页具有读写和执行权限.如果它是可执行文件(即不是dylib),则它必须包含__PAGEZERO段,完全没有任何权限.这是一个安全的保护区域,可防止意外访问虚拟内存的低地址(此处臭名昭著的Null指针异常,以及尝试访问零内存地址的情况).
__TEXT读取可执行文件(通常不包含写入文件)段,其后在虚拟内存中将包含文件表示形式本身.这意味着所有可执行代码都保存在这里.也是不变的数据,例如字符串常量.
顺序可能有所不同,但是通常接下来您将遇到__LINKEDIT只读段.这是dyld用来设置外部加载的函数的段,此处范围太广,无法在此进行介绍,但是有关该主题的答案很多.
最后,我们有一个可读的可写__DATA段,它是进程实际上可以写入的第一个位置.这用于全局/静态变量,即由dyld填充的调用的外部地址.

Let's take a step back - the Mach-o format describes mapping the binary segments into virtual memory. Importantly the memory pages you mentioned have read write and execute permissions. If it's an executable(i.e. not a dylib) it must contain the __PAGEZERO segment with no permissions at all. This is the safe guard area to prevent accessing low addresses of virtual memory by accident (here falls the infamous Null pointer exception and such if attempting to access zero memory address).
__TEXT read executable (typically without write) segment follows which in virtual memory will contain the file representation itself. This implies all the executable code lives here. Also immmutable data like string constants.
The order may vary, but usually next you will encounter __LINKEDIT read only segment. This is the segment dyld uses to setup externally loaded functions, this is too broad to cover here, but there are numerous answers on the topic.
Finally we have the readable writable __DATA segment the first place a process can actually write to. This is used for global/static variables, external addresses to calls populated by dyld.

当它通过LC_UNIXTHREAD或在现代MacOS(10.7+)LC_MAIN中启动时,我们大致介绍了该过程的初始设置.这将启动进程主线程.每个线程必须包含自己的堆栈.它的创建由操作系统处理(包括分配).请注意,到目前为止,该过程根本不了解堆(正是操作系统在为准备堆做了大量工作).

We have roughly covered the process initial setup when it will launch through either LC_UNIXTHREAD or in modern MacOS (10.7+) LC_MAIN. This starts the process main thread. Each thread must contain it's own stack. The creation of it is handled by operating system (including allocating it). Notice so far the process has no awareness of the heap at all (it's the operating system that's doing the heavy lifting to prepare the stack).

因此,到目前为止,总结起来,我们有2个独立的内存源-代表Mach-o结构(大小是固定的,并由可执行结构确定)的进程内存和主线程堆栈(也具有预定义的大小).该过程将运行一个类似C的主函数,任何声明为的局部变量将移动线程堆栈指针,同样,对函数的任何调用(局部和外部)至少应设置要返回的堆栈框架地址.访问全局/静态变量将直接引用__DATA段虚拟内存. 在x86-64程序集中保留堆栈空间如下所示:

So to sum up so far we have 2 independent sources of memory - the process memory representing the Mach-o structure (size is fixed and determined by the executable structure) and the main thread stack (also with predefined size). The process is about to run a C-like main function , any local variables declared would move the thread stack pointer, likewise any calls to functions (local and external) to at least setup the stack frame for return address. Accessing a global/static variable would reference the __DATA segment virtual memory directly. Reserving stack space in x86-64 assembly would look like this:

sub rsp,16

System V/AMD64 ABI(包括MacOS)在堆栈对齐方面有很多出色的解决方案,例如一个

There are some great SO anwers on System V / AMD64 ABI (which includes MacOS) requirements for stack alignment like this one

创建的任何新线程将具有自己的堆栈,以允许为局部变量和调用函数设置堆栈框架.

Any new thread created will have its own stack to allow setting up stack frames for local variables and calling functions.

现在,我们可以介绍分配-通过提供malloc/freelibSystem(又名MacOS C标准库)可以减轻分配.在内部,这由mmap& munmap系统调用-用于管理内存页面的内核API. 可以直接使用这些系统调用,但是可能效率低下,因此malloc/free使用内部内存池来限制系统调用的次数(这很昂贵).

Now we can cover heap allocation - which is mitigated by the libSystem (aka MacOS C standard library) delivering the malloc/free. Internally this is handled by mmap & munmap system calls - the kernel API for managing memory pages. Using those system calls directly is possible, but might turned out inefficient, thus an internal memory pool is utilised by malloc/free to limit the number of system calls (which are costly to make).

您在评论中提到的地址更改是由以下原因引起的:

The changing addresses you mentioned in the comment are caused by:

  1. 用于进程内存的ASLR aka PIE(与位置无关的代码),这是一种安全措施,用于随机分配虚拟内存的起始位置
  2. 操作系统正在准备的线程本地堆栈

这篇关于macOS如何为进程分配堆栈和堆?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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