首页 文章

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

提问于
浏览
6

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

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

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

Heap

  • 包含所有实例变量,动态分配(new / malloc)和仅全局变量

  • 不再使用数据结构堆,使用更复杂的结构

  • 通过内存位置访问,负责分配内存的单个进程

  • 碎片整理和内存分配由操作系统完成(如果是或否,请回答我关于谁管理堆,操作系统或运行时环境的问题)

  • 在进程中可以访问其引用的所有线程之间共享

Stack

  • 仅包含所有局部变量 . (在功能调用时推送)

  • 使用实际的堆栈数据结构进行操作

  • 由于连续的性质,访问速度更快

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

  • 全局变量,它们在哪里被分配? (我的信念是它们被分配在堆上,如果是这样,它们何时被分配,在运行时或编译时,还有一个问题,是否可以清除此内存(如使用删除)?)

  • 堆的结构是什么?堆是如何组织的(由os或运行时环境管理(由C / C编译器设置)) .

  • 堆栈是否包含 ONLY 方法及其局部变量?

  • 每个应用程序(进程)都有一个单独的堆,但如果超过堆分配,那么它是否意味着操作系统无法分配更多内存? (我假设内存不足导致操作系统重新分配以避免碎片)

  • 可以从进程中的所有线程访问堆(我相信这是真的) . 如果是,则所有线程都可以访问实例变量,动态分配的变量,全局变量(如果它们有引用它)

  • 不同的进程,无法访问彼此堆(即使它们被传递了地址)

  • 堆栈溢出崩溃

  • 只有当前线程

  • 当前进程

  • 所有流程

  • 在C / C中,内存是否在堆栈运行时为函数内的块变量分配(例如,如果代码的子块(例如For循环)创建新变量,则在运行时分配在堆栈(或堆)上还是预先分配?)何时删除它们(块级范围,如何维护) . 我对此的看法是,堆栈的所有添加都是在块开始之前在运行时进行的,每当到达该块的结尾时,所有添加到该点的元素都被推送 .

  • CPU对堆栈寄存器的支持仅限于堆栈指针,可通过正常访问内存来递增(弹出)和递减(推送) . (这是真的?)

  • 最后,是主存储器上存在的OS /运行时环境生成的堆栈和堆结构(作为抽象?)

我知道这很多,而且我似乎总是非常困惑,如果你能指出我正确的方向让这些事情得到澄清,我将不胜感激!

2 回答

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

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

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

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

    • 是的 .

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

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

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

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

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

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

    • 你有内核看到的物理内存 .

    • 当进程请求时,内核将物理内存页面映射到虚拟内存页面 .

    • 进程在其自己的虚拟内存空间中运行,不知道系统上的其他进程 .

    • 当进程启动时,它会将可执行文件的部分(代码,全局等)放入其中一些虚拟内存页面中 .

    • 分配器从进程请求页面以满足malloc调用,这个内存构成堆 .

    • 当线程启动时(或进程的初始线程),它会向操作系统询问构成堆栈的几个页面 . (您也可以询问堆分配器,并使用它为堆栈提供的空间) .

    • 当程序运行时,它可以自由访问其地址空间,堆,堆栈等中的所有内存 .

    • 当您尝试访问未映射的内存空间区域时,程序崩溃 . (更具体地说,您从操作系统获得信号,您可以选择处理) .

    • 堆栈溢出往往会导致程序访问这些未映射的区域,这就是堆栈溢出往往会导致程序崩溃的原因 .

  • 7
    • 分配全局变量实际上取决于系统 . 有些系统会将它们静态地放在二进制文件中,有些会将它们分配到堆上,有些系统会将它们分配到堆栈中 . 如果一个全局变量是一个指针,你可以 delete 指向它的值,但是没有办法清除那个内存 . 当应用程序退出时,将自动调用全局变量的析构函数(好吧,也许不使用SIGTERM)

    • I 'm not positive, but I imagine it'由操作系统管理,特别是内核 .

    • 是的,而且仅限于某一点 . 例如,你可以用a,等待它,堆栈溢出(AHH,它就在那里,他说了!)

    • 某些操作系统可能会通过单独的进程强制执行堆大小限制,但通常如果您未能分配内存,则不会留下任何内存 .

    • 所有线程共享一个公共堆,所以是的,它们都可以访问全局变量,动态分配等等 .

    • 一般来说,虽然在一些非常简单的架构上,这可能不是真的 . 在大多数情况下,操作系统在虚拟表的上下文中执行进程,因此您使用的指针值实际上指向的内存地址不同于他们会出现什么 .

    • 当前进程,如果按进程表示操作系统级进程 .

    • 我'm assuming that is correct, but I don' t了解自己 .

    • 这个是我的驾驶室 .

    • 是的,有点 . 正如我之前提到的,大多数操作系统使用vtable将进程指针映射到主内存 . 另外,考虑分页到磁盘(交换)

相关问题