C / C ++中的并发编程,堆栈和堆

时间:2013-01-24 03:16:39

标签: c++ memory-management parallel-processing heap-memory stack-memory

嗯,我很抱歉,如果这感觉就像重复旧问题一样,我已经通过Tanenbaum对Stack Overflow,现代操作系统手册进行了几个问题,并且仍然要清除我对此的怀疑。

首先,我将非常感谢我应该更详细地阅读的任何书籍/资源,以便更好地理解这种结构。我不明白这些是OS书籍,编程语言或架构书籍中通常解释的概念。

在我提出问题之前,我会根据有关堆栈/堆的读数列出我的发现

  • 仅包含所有实例变量,动态分配(new / malloc)和全局变量
  • 不再使用数据结构堆,使用更复杂的结构
  • 通过内存位置访问,负责分配内存的单个进程
  • 碎片整理和内存分配由操作系统完成(如果是或否,请回答我关于谁管理堆,操作系统或运行时环境的问题)
  • 在流程中有权访问其参考的所有线程之间共享

堆栈

  • 仅包含所有局部变量。 (在功能调用时推送)
  • 使用实际的堆栈数据结构进行操作
  • 由于连续性而更快访问

现在,关于我的一些问题。

  1. 全局变量,它们在哪里被分配? (我的信念是它们在堆上分配,如果是这样,它们何时被分配,在运行时或编译时,还有一个问题,是否可以清除此内存(如使用删除)?)
  2. 堆的结构是什么?堆是如何组织的(由os或运行时环境管理(由C / C ++编译器设置)。)
  3. 堆栈是否包含 ONLY 方法及其局部变量?
  4. 每个应用程序(进程)都有一个单独的堆,但如果超过堆分配,那么它是否意味着操作系统无法分配更多内存? (我假设缺少内存导致操作系统重新分配以避免碎片)
  5. 可以从进程中的所有线程访问堆(我相信这是真的)。如果是,则所有线程都可以访问实例变量,动态分配的变量,全局变量(如果它们具有对它的引用)
  6. 不同的进程,无法访问彼此堆(即使它们是传递地址)
  7. 堆栈溢出崩溃
    • 只有当前帖子
    • 当前流程
    • 所有流程
  8. 在C / C ++中,内存是否在堆栈运行时分配给函数中的块变量(例如,如果代码的子块(例如For循环)创建了一个新变量,则在运行期间分配 - 堆栈(或堆)上的时间还是预先分配?)何时删除它们(块级范围,如何维护)。我对此的看法是,堆栈的所有添加都是在块开始之前在运行时进行的,每当到达该块的结尾时,所有添加到该点的元素都被推送。
  9. CPU对堆栈寄存器的支持仅限于堆栈指针,可以通过正常访问内存来递增(弹出)和递减(推送)。 (这是真的吗?)
  10. 最后,主存储器上存在的OS /运行时环境生成的堆栈和堆结构(作为抽象?)
  11. 我知道这很多,而且我似乎总是非常困惑,如果你能指出我正确的方向让这些事情得到澄清,我将不胜感激!

2 个答案:

答案 0 :(得分:7)

  1. 全局变量分配在编译时布局的内存的静态部分中。在输入main之前启动期间初始化值。当然,初始化可以在堆上进行分配(即静态分配的std::string将使结构本身位于静态布局的内存中,但是它包含的字符串数据在启动期间在堆上分配)。在正常程序关闭期间删除这些内容。在此之前你不能释放它们,如果你愿意,你可能想要将值包装在指针中,并在程序启动时初始化指针。

  2. 堆由分配器库管理。 C运行库附带了一个,但也可以使用tcmallocjemalloc之类的自定义代码来代替标准分配器。这些分配器使用系统调用从操作系统获取大型内存,然后在调用malloc时为您提供这些页面的一部分。堆的组织有点复杂,并且在分配器之间有所不同,您可以查看它们在网站上的工作方式。

  3. 是肥胖型。虽然您可以使用像alloca这样的库函数在堆栈上创建一大块空间,并将其用于您想要的任何内容。

  4. 每个进程都有一个单独的内存空间,也就是说,它认为它是独一无二的,并且不存在其他进程。一般来说,如果你要求它,操作系统会给你更多的内存,但它也可以强制限制(比如linux上的ulimit),这时它可以拒绝给你更多的内存。碎片不是操作系统的问题,因为它在页面中提供内存。但是,过程中的碎片可能会导致分配器要求更多页面,即使空白也是如此。

  5. 是的,但是通常有特定于操作系统的方法来创建多个进程可以访问的共享内存区域。

  6. 堆栈溢出本身不会崩溃,它会导致内存值写入可能包含其他值的位置,从而破坏它。对损坏的内存进行操作会导致崩溃。当您的进程访问未映射的内存时(请参阅下面的注释),它会崩溃,而不仅仅是一个线程,而是整个进程。它不会影响其他进程,因为它们的内存空间是隔离的。 (在Windows 95等旧操作系统中并非如此,其中所有进程共享相同的内存空间。)

  7. 在C ++中,堆栈分配的对象在输入块时创建,并在退出块时销毁。虽然堆栈上的实际空间可能分配得不那么精确,但构造和破坏将在这些特定点进行。

  8. x86进程上的堆栈指针可以任意操作。编译器通常只生成向堆栈指针添加空间量的代码,然后为堆栈中的值设置内存,而不是进行一堆推送操作。

  9. 进程的堆栈和堆都存在于同一个内存空间中。

  10. 有关如何组织内存的概述可能会有所帮助:

    • 你有内核看到的物理内存。
    • 当进程请求时,内核将物理内存页面映射到虚拟内存页面。
    • 进程在其自己的虚拟内存空间中运行,不知道系统上的其他进程。
    • 当进程启动时,它会将可执行文件的部分(代码,全局等)放入其中一些虚拟内存页面中。
    • 分配器从进程请求页面以满足malloc调用,此内存构成堆。
    • 当线程启动时(或者进程的初始线程),它会向操作系统询问构成堆栈的几个页面。 (您也可以询问堆分配器,并使用它为堆栈提供的空间)。
    • 当程序运行时,它可以自由访问其地址空间,堆,堆栈等所有内存。
    • 当您尝试访问未映射的内存空间区域时,程序崩溃。 (更具体地说,您从操作系统获得信号,您可以选择处理)。
    • 堆栈溢出往往会导致程序访问这些未映射的区域,这就是堆栈溢出会导致程序崩溃的原因。

答案 1 :(得分:2)

  1. 分配全局变量实际上取决于系统。有些系统会将它们静态地放在二进制文件中,有些会将它们分配到堆上,有些系统会将它们分配到堆栈中。如果全局变量是指针,则可以delete它指向的值,但是否则无法清除该内存。应用程序退出时会自动调用全局变量的析构函数(好吧,可能不会使用SIGTERM)
  2. 我不是肯定的,但我想它是由操作系统管理的,特别是内核。
  3. 是的,只有到了某一点。例如,你不能进行无限递归,因为值会(没有双关语)叠加。你会结束,等待它,堆栈溢出(AHH,就是这样,他说了!)
  4. 某些操作系统可能会通过单独的进程强制执行堆大小限制,但通常如果您未能分配内存,则因为没有内存。
  5. 所有线程共享一个公共堆,所​​以是的,它们都可以访问全局变量,动态分配等等。
  6. 一般来说,虽然在一些非常简单的架构上,这可能不是真的。在大多数情况下,操作系统在虚拟表的上下文中执行该过程,因此您使用的指针值实际上指向的内存地址与它们看起来的内容不同。
  7. 当前流程,如果按流程表示操作系统级流程。
  8. 我认为这是正确的,但我不了解自己。
  9. 这是我的驾驶室。
  10. 是的,有点儿。正如我之前提到的,大多数操作系统使用vtable将进程指针映射到主内存。另外,考虑分页到磁盘(交换)