使用“push”或“sub”x86 指令时如何分配堆栈内存?

2024-10-21 09:14:00
admin
原创
95
摘要:问题描述:我已经浏览了一段时间,并试图了解在执行以下示例时如何将内存分配给堆栈:push rax 或者移动堆栈指针来为子程序的局部变量分配空间:sub rsp, X ;Move stack pointer down by X bytes 我的理解是,堆栈段在虚拟内存空间中是匿名的,即不受文件支持。我还...

问题描述:

我已经浏览了一段时间,并试图了解在执行以下示例时如何将内存分配给堆栈:

push rax

或者移动堆栈指针来为子程序的局部变量分配空间:

sub rsp, X    ;Move stack pointer down by X bytes 

我的理解是,堆栈段在虚拟内存空间中是匿名的,即不受文件支持。

我还了解到,内核不会真正将匿名虚拟内存段映射到物理内存,直到程序实际对该内存段执行某些操作(即写入数据)。因此,在写入之前尝试读取该段可能会导致错误。

在第一个例子中,如果需要,内核将在物理内存中分配一个框架页面。在第二个例子中,我假设内核不会为堆栈段分配任何物理内存,直到程序实际将数据写入堆栈段中的地址。

我在这条路上走对了吗?


解决方案 1:

是的,你基本上是在正确的轨道上。 sub rsp, X有点像“惰性”分配:内核只在#PF页面错误异常之后才执行任何操作,因为页面错误异常会触及新 RSP 上方的内存,而不仅仅是修改寄存器。但你仍然可以认为内存“已分配”,即可以安全使用。

因此,在写入之前尝试读取该段可能会导致错误。

不会,读取不会导致错误。从未写入的匿名页面会被写入时复制映射到物理零页,无论它们是在 BSS、堆栈还是 中mmap(MAP_ANONYMOUS)

有趣的事实:在微基准测试中,请确保为输入数组写入每一页内存,否则您实际上是在重复循环相同的物理 4k 或 2M 零页,并且会获得 L1D 缓存命中,即使您仍然会获得 TLB 未命中(和软页面错误)! gcc 将优化 malloc+memset(0) 以calloc,但std::vector实际上会写入所有内存,无论您是否愿意。 memset全局数组上的没有被优化,所以这是可行的。(或者非零初始化数组将在数据段中进行文件支持。)


请注意,我忽略了映射与有线之间的区别。即,访问是否会触发软/次要页面错误来更新页表,或者是否仅仅是 TLB 未命中并且硬件页表遍历会找到映射(到零页)。

但是 RSP 下方的堆栈内存可能根本没有被映射,因此在不先移动 RSP 的情况下接触它可能会引发无效页面错误,而不是用于解决写时复制问题的“次要”页面错误。


堆栈内存有一个有趣的变化:堆栈大小限制大约为 8MB(ulimit -s),但在 Linux 中,进程的第一个线程的初始堆栈是特殊的。例如,我_start在 hello-world(动态链接)可执行文件中设置了一个断点,并查看了/proc/<PID>/smaps它:

7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
Size:                132 kB
Rss:                   8 kB
Pss:                   8 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         8 kB
Referenced:            8 kB
Anonymous:             8 kB
...

仅引用了 8kiB 的堆栈,并由物理页面支持。这是意料之中的,因为动态链接器不会使用大量堆栈。

甚至只有 132kiB 的堆栈被映射到进程的虚拟地址空间中。 但是特殊的魔法阻止了mmap(NULL, ...)在堆栈可以增长到的 8MiB 虚拟地址空间内随机选择页面。

接触当前堆栈映射之下但在堆栈限制内的内存 会导致内核增加堆栈映射(在页面错误处理程序中)。

(但前提rsp是先调整;红区仅低于 128 字节rsp,因此ulimit -s unlimited接触低于 1GB 的内存不会使rsp堆栈增长到那里,但如果您减少到那里然后接触内存就会rsp。)

这仅适用于初始/主线程的堆栈pthreads仅用于mmap(MAP_ANONYMOUS|MAP_STACK)映射无法增长的 8MiB 块。(MAP_STACK当前为无操作。)因此线程堆栈在分配后无法增长(除非MAP_FIXED在其下方有空间则手动增长),并且不受其影响ulimit -s unlimited


这种阻止其他事物选择堆栈增长区域中的地址的魔法对于不存在mmap(MAP_GROWSDOWN),因此不要使用它来分配新线程堆栈。(否则,最终可能会导致某些东西耗尽新堆栈下方的虚拟地址空间,使其无法增长)。只需分配完整的 8MiB。另请参阅其他线程的堆栈位于进程虚拟地址空间的什么位置?。

MAP_GROWSDOWN确实具有按需增长功能,如手册页中所述mmap(2),但没有增长限制(除了接近现有映射),因此(根据手册页)它基于 Windows 使用的保护页,而不是像主线程的堆栈。

触碰区域底部以下多个页面的内存MAP_GROWSDOWN可能会引发段错误(与 Linux 的主线程堆栈不同)。针对 Linux 的编译器不会生成堆栈“探测”以确保在进行大分配(例如本地数组或 alloca)后按顺序触碰每个 4k 页面,因此这也是MAP_GROWSDOWN堆栈不安全的另一个原因。

编译器确实会在 Windows 上发出堆栈探测。

MAP_GROWSDOWN甚至可能根本不起作用,请参阅@BeeOnRope 的评论。使用它从来都不是很安全,因为如果映射接近其他东西,可能会出现堆栈冲突安全漏洞。所以永远不要用它MAP_GROWSDOWN来做任何事情。我在这里描述 Windows 使用的保护页面机制,因为有趣的是,Linux 的主线程堆栈设计并不是唯一可能的设计。)

解决方案 2:

堆栈分配使用相同的虚拟内存机制来控制地址访问页面错误。例如,如果您当前的堆栈具有7ffd41ad2000-7ffd41af3000以下边界:

myaut@panther:~> grep stack /proc/self/maps                                                     
7ffd41ad2000-7ffd41af3000 rw-p 00000000 00:00 0      [stack]

然后,如果 CPU 尝试在地址7ffd41ad1fff(堆栈顶部边界前 1 个字节)处读取/写入数据,它将生成页面错误,因为操作系统没有提供相应的分配内存块(页面)。 因此push或任何其他以地址为单位的内存访问命令%rsp都将触发页面错误

在页面错误处理程序中,内核将检查堆栈是否可以增长,如果可以,它将分配页面支持错误地址(7ffd41ad1000-7ffd41ad2000)或触发 SIGSEGV(如果超出堆栈 ulimit)。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用