macOS如何为进程分配堆栈和堆?

时间:2019-07-14 02:50:05

标签: macos assembly operating-system

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

2 个答案:

答案 0 :(得分:1)

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

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

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

sub rsp,16

System V / AMD64 ABI(包括MacOS)对堆栈对齐的要求很高,例如one

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

现在,我们可以讨论分配-通过交付libSystem / malloc的{​​{1}}(又名MacOS C标准库)可以减轻分配。在内部,这由freemmap系统调用-用于管理内存页面的内核API来处理。 可以直接使用这些系统调用,但可能效率低下,因此munmap / malloc利用内部内存池来限制系统调用的次数(这很昂贵)。

free应用程序中又引入了另一层内存处理功能,引入了与线程堆栈不同的单个进程堆栈。用于值类型和结构。我尚未调查其内部实现的详细信息。

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

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

答案 1 :(得分:0)

堆栈和堆只是内存。使堆栈成为堆栈或堆或堆的唯一想法是访问堆栈的方式。堆栈和堆的分配方式与所有内存相同:将页面映射到逻辑地址空间。