繁体   English   中英

C ++中多线程进程的内存布局

[英]memory layout of multithreaded process in C++

在多线程进程中如何安排堆栈和堆有点困惑:

  1. 每个线程都有自己的专用堆栈。
  2. 所有线程共享堆
  3. 当程序动态创建线程时(例如Java中的new Thread()),该对象将分配在堆上。

那么堆中是否包含线程对象的内存,这意味着堆中是否包含堆栈(属于线程)?

我们不希望它含糊其词;不想限制线程软件的实现者。

每个线程都有自己的专用堆栈。

当每个线程执行彼此独立的一组功能时,它们需要存储返回地址等,因此每个线程都需要自己的堆栈。

所有线程共享堆

这是实现它的最简单方法。 这也意味着所有线程共享一个公共的内存块,以便每个线程可以简单地通过修改内存来与其他线程通信。

当程序动态创建线程时(例如Java中的new Thread()),该对象将分配在堆上。

您在问题1中提到的堆栈。我们需要为其保留内存。 因此,我们将堆的一部分分配给线程,并说使用这块内存来实现您的堆栈。 (并不是说它是这样做的,但这是一种简单的方法)。

那么堆中是否包含线程对象的内存,这意味着堆中是否包含堆栈(属于线程)?

在单线程程序中,有空间将堆栈实现为堆块。 堆栈和堆的概念是分开的,并且彼此接近。 一个概念。 还没有定义如何实现它们,也没有理由我们不能在堆内部实现堆栈。 有关更多信息,请参见此问题: 堆栈增长方向

将“堆栈”视为任何其他数据结构。 它可以以多种方式实现。

这是2000年前左右在C和C ++程序中堆栈的典型实现的描述。 大多数人还是这样做:

存在连续的内存地址范围,称为“堆栈”。 通常,在具有内存控制器的系统上(对于Intel而言,这意味着80386及更高版本),直到使用了此内存地址范围的页面,才将它们分配给物理内存。 通常,此连续的地址范围发生在地址空间的末尾。

通常在内存区域的末尾有一个堆栈指针。 当创建一个新的堆栈框架时,堆栈指针会减小该框架的大小。 CPU具有专门为此操作设计的指令。 如果访问的内存区域没有分配任何类型的物理内存,则操作系统将处理页面错误并查找一些内存以分配给当前使用的页面。

所有未传递到寄存器中的局部变量和函数参数都会进入堆栈帧。

对于多线程程序,此方案不起作用,因此通常使用mallocnew分配内存区域,并通过调用开始一个新线程,该调用采用指向该内存区域及其大小的指针。 如果新线程需要的堆栈空间比您分配的更多,那么可能会发生各种可怕的事情,包括线程只是踩踏一些随机内存,其中包括“在堆上”分配的其他变量。

但是,到目前为止,这并不是实现堆栈的唯一方法。 例如,您可以将堆栈实现为链接列表,列表的每个节点都是堆栈框架。 支持称为“ continuation ”的构造的语言经常会这样做。 实际上,它们通常使用DAG,因为单个堆栈帧可能会产生多个同时有效的其他堆栈帧。

可以做的另一件事是,在其中途中,您的节点只是大的内存区域,每个内存区域包含几个堆栈帧。 当创建一个新的框架时,该框架将超出节点的范围,另一个节点将被分配。

或者,所有局部变量都可以分配新的变量或类似的变量,并在超出范围时销毁。 编译器可以使这种情况在后台发生。

因此,担心堆栈的确切位置或内存在幕后的分配方式,尤其是像Java这样的语言甚至没有C或C ++的指针的语言,实在是很愚蠢的。 不同的完全兼容的JVM之间甚至可能有所不同。

我会说,一般而言,C ++中的pthreads以我在介绍C和C ++的历史工作方式的本节的最后一段中描述的多线程程序的方式来实现堆栈。 它们通常还具有“保护页”,在分配给堆栈的区域的开头有意未映射的页面,这样用尽堆栈空间的程序通常将SEGV。 (实际上,这显然是对错误点的过分简化,请参阅Ben Voigt的评论以了解对保护页面的实际使用)。

每个堆栈都是在堆上生成的,只有少量内核从真正的“一个”堆栈运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM