【问题标题】:How can there be multiple call stacks allocated at the same time? How does the stack pointer change between threads?如何同时分配多个调用堆栈?线程之间的堆栈指针如何变化?
【发布时间】:2023-03-15 19:30:01
【问题描述】:

我的理解总结:
顶部内存地址用于(我最初以为只有一个调用堆栈)堆栈,堆栈向下增长(What and where are the stack and heap?

但是,每个线程都分配了自己的堆栈,因此内存中应该有多个调用堆栈 (https://stackoverflow.com/a/80113/2415178)

应用程序可以共享线程(例如,关键应用程序正在使用主线程),但可以同时运行多个线程。

有一个名为 sp 的 CPU 寄存器跟踪堆栈指针,即调用堆栈的当前堆栈帧。

这就是我的困惑:
当应用程序启动时,是否分配了应用程序所需的所有调用堆栈(如果甚至可能知道)?或者调用堆栈是否会随着应用程序剥离新线程而动态分配/取消分配?如果是这种情况,(我知道堆栈具有固定大小),新堆栈是否只是在先前堆栈的正下方分配 - 所以你最终会在内存的顶部地址中获得堆栈堆栈?还是我只是从根本上误解了调用堆栈是如何创建/使用的?

我是一名 OS X 应用程序开发人员,因此我对如何创建调用堆栈的视觉参考来自 Xcode 的堆栈调试器:

现在我意识到,这里的情况很可能是 OS X 独有的,但我希望操作系统之间的约定是相似的。
似乎每个应用程序都可以在多个线程上执行代码,甚至可以衍生出属于该应用程序的新工作线程——每个线程都需要一个调用堆栈来跟踪堆栈帧。

这让我想到了最后一个问题: 如果有多个调用堆栈,sp 寄存器如何工作?它仅用于主调用堆栈吗? (大概是内存中最顶层的调用栈,与操作系统的主线程相关联)[https://stackoverflow.com/a/1213360/2415178]

【问题讨论】:

    标签: multithreading macos memory memory-management stack


    【解决方案1】:

    应用程序启动时是否分配了应用程序所需的所有调用堆栈(如果可能知道的话)?

    没有。通常,每个线程的堆栈都是在创建线程时分配的。

    或者调用堆栈是否会随着应用程序启动新线程而动态分配/取消分配?

    是的。

    如果是这种情况,(我知道堆栈具有固定大小),新堆栈是否只是在先前堆栈的正下方分配 - 所以你最终会在内存的顶部地址中获得堆栈堆栈?还是我只是从根本上误解了调用堆栈是如何创建/使用的?

    它因人而异。但是堆栈只需要位于该特定进程的内存映射中足够大的可用地址空间块的顶部。它不必位于最顶端。如果您需要 1MB 的堆栈空间,而您有 1MB,则只需保留 1MB 并让堆栈从其顶部开始。

    如果有多个调用堆栈,sp 寄存器如何工作?只用于主调用栈吗?

    一个 CPU 的寄存器集与一次可以运行的线程一样多。当正在运行的线程切换时,离开线程的堆栈指针被保存,新线程的堆栈指针被恢复——就像所有其他寄存器一样。

    没有“操作系统的主线程”。有一些内核线程只执行内核任务,但用户空间线程也运行在内核空间中以运行操作系统代码。纯内核线程在内核内存的某个地方有自己的堆栈。但就像普通线程一样,它不必位于最顶端,堆栈指针只需从用于该堆栈的块中的最高地址开始。

    【讨论】:

    • 那么每个线程都有自己的“寄存器集”,当该线程在处理单元上执行时,该处理单元的寄存器将设置为当前线程的“寄存器集”?那么在 4 核 CPU 中,每个处理单元一次持有一个“寄存器集”,并在它们正在执行的线程交换时将它们交换出去?
    • @AO 是的,没错。如果 CPU 一次可以运行四个线程(例如,因为它有四个物理内核),那么它也有四组寄存器。当操作系统切换线程时,它会保存离开线程的上下文(包括它的寄存器)并加载新运行的线程的上下文。
    • 这是必须发生的令人难以置信的处理量。感谢您的回答!这有助于我理解很多
    • 有趣的是,这并不是现代 CPU 上切换线程成本高昂的真正原因。切换线程的成本很高,主要是因为所有内核的缓存都有旧线程正在使用的代码和数据,而新线程必须“重新加热缓存”。
    • 有道理。您是否碰巧有关于您认为特别有趣的主题的资源(博客、文章等)?
    【解决方案2】:

    没有“操作系统的主线程”这样的东西。每个进程都有自己的一组线程,这些线程特定于该进程,而不是共享的。通常,在任何给定时间点,系统上的大多数线程都将暂停等待输入。

    进程中的每个线程都有自己的堆栈,该堆栈在线程创建时分配。大多数操作系统会在每个堆栈之间留出一些空间,以允许它们在需要时增长,并防止它们相互碰撞。

    每个线程也有自己的一组 CPU 寄存器,包括一个堆栈指针(指向该线程堆栈中的一个位置)。

    【讨论】:

    • 感谢您的回答!在这种情况下,如果您打开了 100 个应用程序,那么可能会有数千个线程闲置在内存中的调用堆栈中 - 有效地阻塞了吗?
    • 并非所有都在同一个地址空间中,但绝对是的。例如,top 报告说我的计算机当前有大约 1700 个线程处于活动状态。
    • @AO 注意这里的“内存”是指虚拟内存,不一定是RAM。
    猜你喜欢
    • 2014-07-05
    • 2015-11-16
    • 2021-09-01
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    • 2014-10-16
    • 2017-05-06
    • 2011-10-09
    相关资源
    最近更新 更多