【问题标题】:How does a computer use just a few registers?计算机如何只使用几个寄存器?
【发布时间】:2011-03-25 13:22:56
【问题描述】:

我对汇编略知一二,通用寄存器有 4 或 8 个左右。计算机上的所有程序如何使用这么多的寄存器,尤其是多线程和所有东西?

【问题讨论】:

  • 我喜欢将寄存器视为处理器的局部变量。我不倾向于在当前函数的范围内有大量变量。它们中的大多数都在内存中的某个地方,在程序的其他部分。
  • @Merlyn:没错,但可能有点误导,因为本地变量(在没有编译器优化的情况下)通常存储在堆栈中。
  • @bcat:是的,这个概念并不完美。不同的架构也会有不同的行为(尽管这可以被视为编译器优化)。
  • @bcat:我发现最有用的心智模型是本地 vars = 可以保存在寄存器中的值,如果您的 regs 用完,您可以将它们溢出到堆栈中。在调试模式下编译 comp 总是为了一致的调试。 (你说得对,C 语言模型给每个对象一个地址,除了register vars,但实际上在 asm 中 programming 我建议的是正确的开始方式。)

标签: multithreading assembly cpu-registers


【解决方案1】:

多线程本身不会影响使用中的寄存器数量。当一个线程被换出时,它通常会将其寄存器保存到内存中,并且下一个要运行的线程会从之前的保存中加载这些寄存器。

一个示例是具有线程控制块结构 (TCB) 的系统。该结构将包含(在线程未运行时)保存的指令指针、堆栈指针、通用寄存器、浮点寄存器、线程统计信息等。简而言之,一切都需要将线程完全恢复到它被换出以运行另一个线程时的状态。

并不是计算机中发生的所有事情都在寄存器中完成。现代编译器可以优化代码,以便将使用最多的数据项保存在寄存器中,但绝大多数数据保存在内存中,并且只在需要时才购买到寄存器中。

我读过的关于这个主题的最好的书是Tanenbaum's"Structured Computer Organization",它从层级角度检查计算机,从数字逻辑层到操作系统层,每一层都建立在上一层之上。

           

除此之外:我的梦想是有朝一日写一本像这样的书,涵盖一切,从夸克级别到 Emacs :-)

【讨论】:

  • +1 为 Tanenbaum,不是因为他是 Tanenbaum,而是因为那本书非常棒。
  • 业内很少有人能像 Tanenbaum 一样评价高(只有 Knuth 是唯一一个立即浮现在脑海中的人)。两者都为他们多产的书籍和软件做出了很大贡献。我仍然有这本书的 2/E,这是我在 Uni (looong 前)得到的。
  • 哦,我完全同意。我只是说我喜欢这本书是因为它本身的价值,而不仅仅是因为它的作者是 CS 中非常重要的人物(Tanenbaum 肯定是)。是的,我在大学时也必须阅读这本书,我很高兴我的教授选择它而不是另一本计算机系统书籍。我仍然惊讶于它如何清晰地呈现材料而丝毫不降低它。
  • 封面使它看起来像一本 Richard Scarry 的书。从它的封面来看,我不知道是不是……等等……有人告诉我我不应该那样做……
【解决方案2】:

这是一个相当复杂的问题,答案取决于您的 CPU 架构。

在过去的好日子里,你是对的——确实只有几个通用寄存器。如今,CPU 和编译器通过register renaming 等技术与您的通用寄存器玩“three-card-monte”游戏。

尽管在简单的架构上,当上下文切换发生时,寄存器确实会被复制到 [缓存] 内存中,但像 SMT 这样的技术会“愚弄”操作系统,使其认为内核数量比实际数量多。

但对您的问题最一般的回答是数据被移入和移出寄存器很多。这就是为什么您在任何给定的汇编程序中看到的大量指令都是“MOV”指令的原因。 CPU 和编译器设计人员花费大量时间和金钱来优化他们的设计,这样您就不会将数据从主存储器(慢速)移动到寄存器中——他们试图尽可能多地保持数据缓存。如此大量的“MOV”指令就是内存延迟和总线速度对整个计算机的性能如此重要的原因。

【讨论】:

    【解决方案3】:

    实际上很有趣的是,计算机如何能够使用这么少的寄存器来完成它所做的一切。

    在汇编级别进行非常聪明的编程(通常是由于非常聪明的编译器)允许如此高效地使用如此少的寄存器。

    如果仅使用提供的几个寄存器就无法解决问题,程序通常会将其寄存器“溢出”到主内存堆栈中。通过记住我们将溢出的寄存器放在堆栈中的哪个位置,我们可以轻松地将它们取回。

    当我们需要的寄存器用完时,我们只需将它们存储在堆栈中,这为我们提供了 FAR 比我们大多数程序所需的更多空间。

    在多线程的特定情况下,我们只需将所有寄存器保存到内存中,然后我们就可以为其他线程提供一个干净的状态。

    【讨论】:

      【解决方案4】:

      每次线程(或进程)换出时,操作系统内核会将所有寄存器压入堆栈,进入通常称为进程控制块的数据结构。然后,当线程/进程换回时,寄存器的数据会从 PCB 中读取并从堆栈中弹出到寄存器中。


      x86 内部也有内部寄存器和映射表,用于设置虚拟寄存器表以保留 IA32 指令集架构,同时在设计超标量架构和复杂的指令调度算法方面具有更大的灵活性。

      另外,指令集通常有 loadstore 指令,它们与指向内存的指针结合使用,允许将数据从寄存器存储到内存中。这就是 Load-Store 机器一词的由来,即没有直接在内存上操作的指令的计算机。

      有些计算机确实有对内存进行操作的指令;有些是基于堆栈的。这取决于设计人员和对硬件的约束。

      【讨论】:

        【解决方案5】:

        这是计算机的其他存储(尤其是 RAM)的用途之一:在寄存器中保存和恢复数据位。

        当一个线程被关闭以便另一个线程可以运行时。第一个线程的寄存器状态保存在某处(在堆栈或其他数据结构上),第二个线程的寄存器状态从之前保存的位置恢复。 RAM 足够快,可以在一秒钟内发生数千次这样的切换,但需要足够的时间,如果您不必要地交换线程,它可能会影响性能。

        另一个非常非常常见的情况是局部变量——如果一个局部变量的使用时间足够短,它可能永远不会存在于寄存器之外。但是,在许多情况下,可能需要将局部变量从寄存器保存到内存位置,以便可以将其他值加载到寄存器中并在寄存器中进行操作。实际上几乎所有变量都会发生同样的情况,而不仅仅是局部变量(但局部变量更有可能永远不会存在于内存中)。

        【讨论】:

          【解决方案6】:

          其他变量和线程堆栈通常存储在受保护的内存空间中,需要时可以将它们调用到寄存器中。

          您可能需要查看The Elements of Computing Systems 这本书,以更好地了解您的计算机 CPU 的工作原理。本书设置为一系列项目,您从 NAND 门到 CPU、汇编器、简单编译器,再到小型操作系统。这对于了解计算机的所有部件如何组合在一起非常宝贵。

          【讨论】:

            【解决方案7】:

            您必须意识到即使是简单的事情也会执行数千到数百万条汇编指令。这些寄存器经常交换它们的值。

            【讨论】:

              猜你喜欢
              • 2019-07-03
              • 2013-08-05
              • 2019-02-16
              • 2014-03-05
              • 1970-01-01
              • 2020-05-04
              • 1970-01-01
              • 2013-06-16
              • 1970-01-01
              相关资源
              最近更新 更多