我不得不承认,这两个概念一开始可能看起来非常复杂和相似。有时他们的教学也很混乱。我认为可以在 osdev.org 上找到一个很好的参考:Segmentation Paging
为了完整起见,我在这里也尝试解释一下,但我不能保证正确性,因为我已经几个月没有开发操作系统了。
旧 16 位时代的分割
分割是两个概念中较老的一个,在我看来它更令人困惑。分段适用于——顾名思义——段。段是特定大小的连续内存块。要访问每个段内的内存,我们需要一个偏移量。这使得总共有两个地址分量,它们实际上存储在两个寄存器中。分段的一个想法是扩大只有 16 位寄存器的内存。另一种是某种保护,但没有分页那么复杂。
因为我们现在使用两个寄存器来访问内存,所以我们可以将内存分成块——如上所述,即所谓的段。考虑 1MB (2^20) 的内存。这可以分成每 16 个字节的 65536 个(2^16,因为是 16 位寄存器)段。当然,我们也有用于偏移的 16 位寄存器。用 16 位寻址 16 字节是非常没用的,因此决定段可以重叠(我认为当时也有性能和编程原因)。
以下公式用于通过分段访问 1MB 内存:
Physical address = (A * 0x10) + B
这意味着该段将是偏移量的 16 倍。这也意味着可以通过多种方式访问地址 0x0100,例如通过 A=0x010 和 B=0x0,也可以通过 A=0x0 和 B=0x0100。
这是旧 16 位时代的分段。
如果您查看汇编程序或自己尝试一些东西,您会发现它们甚至在汇编程序中有所谓的寄存器:CS 和 DS(代码段和数据段)。
32 位天分段
后来引入了所谓的全局描述符表(GDT)。这是一个全局表(在 RAM 中的特定位置),其中给出了段号和内存地址以及每个段的其他几个选项。这使我们更接近分页的概念,但仍然不一样。
所以现在程序员自己可以决定段应该从哪里开始。还有一个新概念是,在 GDT 中,人们可以决定一段应该有多长。所以不是每个段都必须是 64kB 长(2^16,因为 16 位寄存器),但程序员可以定义限制。您可以有重叠的段,也可以有完全分开的段。
现在访问 A:B 时(仍然是两个用于访问内存的寄存器),A 将是 GDT 中的条目。因此,我们将在 GDT 中查找 A'th 条目,并查看该段从哪个内存位置开始以及它有多大。然后我们检查 B(偏移量)是否在允许的内存区域内。
寻呼
现在分页与较新的分段方法没有太大区别,但在分页时,每个页面都有固定的大小。因此限制不再是可编程的,每页(当前)有 4kb。此外,与分段不同,逻辑地址空间可以是连续的,而物理地址是连续的。
分页还使用表格来查找内容,并且您仍然将逻辑地址分成几部分。第一部分是页表中条目的编号,第二部分是偏移量。但是,现在偏移量具有 12 位的固定长度来访问 4kb。您也可以有两个以上的部分,然后将使用多个页表。两级页表很常见,对于 64 位系统,我认为甚至三级页表也很常见。
结尾
我希望我能够至少解释一下,但我认为我的解释也令人困惑。最好的办法是深入内核编程,并在启动操作系统时尝试实现最基本的东西。然后你会发现一切,因为由于向后兼容,一切仍然在我们的现代 PC 上。