我对汇编知之甚少,通用寄存器有 4 或 8 个左右。计算机上的所有程序如何使用这么多的寄存器工作,尤其是多线程和一切?
7 回答
多线程本身不会影响正在使用的寄存器数量。当一个线程被换出时,它通常会将其寄存器保存到内存中,并且下一个要运行的线程会从之前的保存中加载这些寄存器。
一个示例是具有线程控制块结构 (TCB) 的系统。该结构将包含(在线程未运行时)保存的指令指针、堆栈指针、通用寄存器、浮点寄存器、线程统计信息等。简而言之,一切都需要将线程完全恢复到它被换出以运行另一个线程时的状态。
并非计算机中发生的所有事情都在寄存器中完成。现代编译器可以优化代码,以便将使用最多的数据项保存在寄存器中,但绝大多数数据保存在内存中,并且仅在需要时才购买到寄存器中。
我读过的关于这个主题的最好的书是Tanenbaum 的 “结构化计算机组织”,它从层级角度检查计算机,从数字逻辑层到操作系统层,每一层都建立在前一层之上。
顺便说一句:我的梦想是有一天写一本像这样的书,涵盖从夸克级别到 Emacs 的所有内容:-)
The other variables and thread stacks are usually stored in protected memory space, where they can be called into registers when needed.
You may want to check out the book The Elements of Computing Systems for a good understanding of how your computer's CPU works. The book is set up as a series of projects where you work up from a NAND gate to a CPU, assembler, simple compiler, and on to a small operating system. It's invaluable in understanding how all your computer's parts fit together.
每次线程(或进程)换出时,操作系统内核会将所有寄存器压入堆栈,形成一个通常称为进程控制块的数据结构。然后,当线程/进程换回时,寄存器的数据从 PCB 中读取并从堆栈中弹出到寄存器。
x86 内部还有内部寄存器和映射表,可以设置虚拟寄存器表以保留 IA32 指令集架构,同时具有更大的灵活性来设计超标量架构和复杂的指令调度算法。
Also, instruction sets usually have a load and store instruction, which is used in conjunction with pointers to memory, allowing data to be stored from registers into memory. Which is where the term Load-Store machine comes from, ie, a computer that doesn't have instructions that operate directly on memory.
Some computers do have instructions that operate on memory; some are stack-based. It depends on the designers and the constraints being placed on the hardware.
您必须意识到即使是简单的事情也会执行数千到数百万条汇编指令。这些寄存器经常交换它们的值。
It is actually pretty interesting how the computer is capable of using so few registers to accomplish everything that it does.
It is really clever programming at the assembly level (typically due to terrifically clever compilers) that allow so few registers to be used so efficiently.
If a problem is impossible to complete with only the few registers provided, the program will typically "spill" its registers into the main memory stack. By remembering where on the stack we put our spilled registers we can easily retrieve them back.
When we run out of the registers we need we simply store them on the stack, which gives us FAR more space than most of us need for our programs.
In the specific case of multi-threading, we just save all of our registers to memory, then we have a clean slate for the other threads.
That's one of the things that the computer's other storage, particularly RAM, is used for: to save and restore bits of data in and out of registers.
When a thread is switched away so another thread can run. the first threads register state is saved somewhere (on the stack or some other data structure), and the seconds thread's register state is restored from wherever it was saved before. RAM is fast enough that thousands of these switches can occur in a second, but takes enough time that if you're swapping threads needlessly it can impact performance.
Another very, very common occurrence is local variables - if a local variable is used for a short enough period of time, it may never exist outside of a register. however, in many cases, a local variable might need to be saved from the register to a memory location so some other value can be loaded into and manipulated in a register. The same actually happens for pretty much any variables, not just locals (but it's much more likely for a local to never have an existence in memory).
That's a pretty involved question and the answer depends on your CPU architecture.
In the good ol' days, you were right -- there were indeed just a few general purpose registers. Nowadays, the CPU and the compiler plays a game of "three-card-monte" with your general purpose registers through techniques like register renaming.
Though on simple architectures it is true that registers get copied to [cache] memory when a context-switch happens, techniques like SMT "fool" the OS into thinking there are more cores than they actually are.
但对您的问题最一般的回答是数据被大量移入和移出寄存器。这就是为什么您在任何给定汇编程序中看到的大量指令都是“MOV”指令的原因。CPU 和编译器设计人员花费大量时间和金钱来优化他们的设计,这样您就不会将数据从主存储器(慢速)移动到寄存器中——他们试图尽可能多地保持数据缓存。大量的“MOV”指令是内存延迟和总线速度对整个计算机的性能如此重要的原因。
