119

所以我知道以下寄存器及其用途应该是什么:

  • CS = 代码段(用于 IP)

  • DS = 数据段(用于 MOV)

  • ES = Destination Segment(用于MOVS等)

  • SS = 堆栈段(用于 SP)

但是以下寄存器的用途是什么?

  • FS =“文件段”?

  • GS = ???

注意:我不是在询问任何特定的操作系统——我是在询问它们打算被 CPU 用于什么,如果有的话。

4

6 回答 6

132

有它们的用途,以及它们用于 Windows 和 Linux 的用途。

段寄存器背后的初衷是允许程序访问许多不同的(大)内存段,这些段原本是独立的,并且是持久虚拟存储的一部分。这个想法来自1966 年的 Multics 操作系统,它将文件视为简单的可寻址内存段。没有 BS“打开文件,写入记录,关闭文件”,只是“将此值存储到该虚拟数据段中”并刷新脏页。

我们目前的 2010 年操作系统是一个巨大的倒退,这就是为什么它们被称为“太监”。只能寻址进程空间的单个段,提供所谓的“平坦(恕我直言)地址空间”。x86-32 机器上的段寄存器仍然可以用于真正的段寄存器,但没有人打扰(英特尔前总裁安迪·格鲁夫(Andy Grove)在上个世纪有一个相当著名的公众契合,当时他在所有英特尔工程师花费精力和他的钱来实现这个功能,没有人会使用它。去吧,安迪!)

AMD 在走向 64 位时决定他们不在乎是否取消了 Multics 作为选择(这是慈善的解释;无情的是他们对 Multics 一无所知),因此禁用了 64 位模式下段寄存器的一般功能。仍然需要线程来访问线程本地存储,并且每个线程都需要一个指针......在可立即访问的线程状态(例如,在寄存器中)的某个地方......来线程本地存储。由于 Windows 和 Linux 在 32 位版本中都为此目的使用了 FS 和 GS(感谢 Nick 的澄清),AMD 决定让 64 位段寄存器(GS 和 FS)基本上仅用于此目的(我想你可以使它们指向您进程空间中的任何位置;我不知道应用程序代码是否可以加载它们)。

恕我直言,让每个线程的内存映射具有作为其线程本地存储的绝对虚拟地址(例如,0-FFF)会在架构上更漂亮(不需要 [segment] 寄存器指针!);早在 1970 年代,我就在一个 8 位操作系统中做到了这一点,它非常方便,就像有另一个大堆寄存器可以使用一样。

所以,段寄存器现在有点像你的附录。他们服务于一个退化的目的。对我们的集体损失。

不了解历史的人注定不会重蹈覆辙;他们注定要做一些更愚蠢的事情。

于 2012-05-30T05:15:28.027 回答
54

寄存器FSGS是段寄存器。它们没有处理器定义的用途,而是由运行它们的操作系统赋予用途。在 Windows 64 位中,GS寄存器用于指向操作系统定义的结构。 FS并且GS通常被操作系统内核用来访问线程特定的内存。在 Windows 中,GS寄存器用于管理线程特定的内存。linux 内核用于GS访问特定于 cpu 的内存。

于 2012-05-30T05:06:23.910 回答
17

FS用于指向 windows 进程上的线程信息块 (TIB)。

一个典型的例子是(SEH),它存储了一个指向回调函数的指针 FS:[0x00]

GS通常用作指向线程本地存储 (TLS) 的指针。您之前可能见过的一个示例是堆栈金丝雀保护(stackguard),在 gcc 中您可能会看到如下内容:

mov    eax,gs:0x14
mov    DWORD PTR [ebp-0xc],eax
于 2018-09-25T04:51:13.690 回答
8

TL;博士;

“FS”/“GS”寄存器的用途是什么?

只需访问默认数据段 (DS) 之外的数据。和ES一模一样。


长读:

所以我知道以下寄存器及其用途应该是什么:

[...]

好吧,差不多,但 DS 不是“某些”数据段,而是默认的。默认情况下进行所有操作的位置 (*1)。这是所有默认变量所在的位置 - 本质上是databss. 这在某种程度上是 x86 代码相当紧凑的部分原因。所有最常访问的基本数据(加上代码和堆栈)都在 16 位速记距离之内。

ES 用于访问其他所有内容 (*2),DS 的 64 KiB 之外的所有内容。像文字处理器的文本、电子表格的单元格或图形程序的图片数据等等。与通常假设的不同,这些数据不会被访问太多,因此需要前缀比使用更长的地址字段伤害更小。

同样,在执行字符串操作时可能必须加载(和重新加载)DS 和 ES 只是一个小烦恼——这至少被当时最好的字符处理指令集之一所抵消。

真正痛苦的是当用户数据超过 64 KiB 并且必须开始操作时。虽然某些操作一次只对单个数据项执行(想想A=A*2),但大多数操作需要两个 ( A=A*B) 或三个数据项 ( A=B*C)。如果这些项目驻留在不同的段中,ES 将在每​​个操作中重新加载几次,从而增加了相当多的开销。

一开始,使用 8 位世界 (*3) 的小程序和同样小的数据集,这没什么大不了的,但它很快就成为了主要的性能瓶颈——对程序员来说更是一个真正的痛苦(和编译器)。随着 386 英特尔终于通过增加两个段来缓解压力,因此任何系列一元二元三元运算,元素分布在内存中,都可以在不重新加载 ES 的情况下进行。

对于编程(至少在汇编中)和编译器设计来说,这是一个相当大的收获。当然,还可以有更多,不过三个瓶颈基本就没有了,不用过分。

明智地命名字母 F/G 只是 E 之后的字母延续。至少从 CPU 设计的角度来看,没有任何关联。


*1 - 将 ES 用于字符串目标是一个例外,因为只需要两个段寄存器。没有它们就没有多大用处——或者总是需要一个段前缀。这可能会扼杀一个令人惊讶的功能,即使用(非重复)字符串指令由于其单字节编码而导致极端性能。

*2 - 所以事后看来,“其他所有部分”会比“额外部分”更好地命名。

*3 - 始终重要的是要记住,8086 仅作为8800完成之前的权宜之计,主要用于嵌入式世界以保持 8080/85 客户的参与。

于 2020-05-12T23:05:22.530 回答
3

根据 Intel 手册,在 64 位模式下,这些寄存器旨在用作某些线性地址计算中的附加基址寄存器。我从第 3.7.4.1 节(第 4 卷中的第 86 页)中提取了这个。通常当 CPU 处于这种模式时,线性地址与有效地址相同,因为这种模式下通常不使用分段。

因此,在这个平面地址空间中,FS 和 GS 不仅在寻址本地数据,而且在寻址某些操作系统数据结构(pg 2793,第 3.2.4 节)中发挥作用,因此这些寄存器旨在供操作系统使用,但是那些特定的设计者决定。

在 32 位和 64 位模式下使用覆盖时有一些有趣的技巧,但这涉及到特权软件。

从“初衷”的角度来看,这很难说,除了它们只是额外的寄存器。当 CPU 处于实地址模式时,这就像处理器以高速 8086 运行,这些寄存器必须由程序显式访问。为了真正的 8086 仿真,您应该在虚拟 8086 模式下运行 CPU,并且不会使用这些寄存器。

于 2019-04-26T02:04:36.887 回答
0

FS 和 GS 段寄存器在 80386 处理器下的 16 位实模式或 16 位保护模式下非常有用,当时只有 64KB 段,例如在 MS-DOS 中。

当 80386 处理器于 1985 年推出时,在 MS-DOS 下具有 640KB RAM 的 PC 计算机很常见。RAM 很昂贵,而且 PC 大多在 MS-DOS 下以实模式运行,最大 RAM 量。

因此,通过使用 FS 和 GS,您可以有效地从程序中寻址两个 64KB 的内存段,而无需在需要寻址其他段而不是加载到 DS 或 ES 中时更改 DS 或 ES 寄存器。本质上,Raffzahn 已经回答说这些寄存器在处理分散在内存中的元素时很有用,以避免一直重新加载其他段寄存器,如 ES。但我想强调的是,这仅与实模式或 16 位保护模式下的 64KB 段有关。

16 位保护模式是一种非常有趣的模式,它提供了从那时起从未见过的特性。这些段的长度可以在 1 到 65536 字节之间。每次内存访问的范围检查(检查段大小)由 CPU 实现,它在访问超出该段选择器表中指定的段大小的内存时引发中断。这防止了硬件级别的缓冲区溢出。您可以为每个内存块分配自己的段(对总数有一定的限制)。有像 Borland Pascal 7.0 这样的编译器使用自己的 DOS 扩展器制作在称为 DOS 保护模式接口 (DPMI) 的 16 位保护模式下在 MS-DOS 下运行的程序。

80286 处理器具有 16 位保护模式,但没有 FS/GS 寄存器。所以程序在使用这些寄存器之前必须先检查它是否在 80386 下运行,即使是在真正的 16 位模式下也是如此。请参阅使用 FS 和 GS 注册 MS-DOS 实模式程序的示例

于 2021-02-04T23:17:13.513 回答