「 FS 」/「 GS 」登记册的用途是什么?

因此,我知道下列寄存器及其用途应该是什么:

  • 代码段(用于 IP)

  • DS = 数据段(用于 MOV)

  • 目的地段(用于 MOVS 等)

  • SS = 栈段(用于 SP)

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

  • 文件段?

  • 我不知道你在说什么?

注意: 我是 没有,询问任何特定的操作系统——如果有的话,我询问它们将被 CPU 用来做什么。

107815 次浏览

寄存器 FSGS是段寄存器。它们没有处理器定义的用途,而是由运行它们的操作系统赋予的用途。在 Windows64位中,GS寄存器用于指向操作系统定义的结构。操作系统内核通常使用 FSGS来访问特定于线程的内存。在窗口中,GS寄存器用于管理特定于线程的内存。Linux 内核使用 GS访问特定于 CPU 的内存。

这就是它们的用途,也是 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)基本上只用于这个目的(我认为你可以让它们指向你的进程空间中的任何地方; 我不知道应用程序代码是否可以加载它们)。英特尔在他们的恐慌,以不失去市场份额 AMD 在64位,安迪退休后,决定只是复制 AMD 的计划。

如果让每个线程的内存映射都有一个绝对的虚拟地址(例如,0-FFF) ,作为它的线程本地存储(不需要[段]寄存器指针) ,那么在架构上会更美观我在20世纪70年代的一个8位操作系统中做到了这一点,它非常方便,就像有另一个大的寄存器堆栈可以工作一样。

段寄存器现在有点像你的阑尾。它们有退化的作用。敬我们的集体损失。

那些不懂历史的人并不是注定要重蹈覆辙,他们注定要做一些更愚蠢的事情。

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

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

GS 通常用作指向线程本地存储(TLS)的指针。 一个你之前可能看到过的例子是堆栈金丝雀保护(stackGuard) ,在 gcc 中你可能会看到这样的东西:

mov    eax,gs:0x14
mov    DWORD PTR [ebp-0xc],eax

根据英特尔手册,在64位模式下,这些寄存器打算用作一些线性地址计算中的附加基寄存器。我从3.7.4.1(pg。在4卷集中是86)。通常当 CPU 处于这种模式时,线性地址和有效地址是一样的,因为在这种模式下通常不使用分段。

因此,在这个平面地址空间中,FS & GS 不仅在寻址本地数据,而且在寻址某些操作系统数据结构(pg 2793,3.2.4节)方面发挥作用,因此这些寄存器将被操作系统使用,无论这些特定的设计者如何决定。

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

从“原始意图”的角度来看,这很难说,除非它们只是额外的寄存器。当 CPU 在 真实地址模式真实地址模式中时,这就像处理器以高速8086运行一样,这些寄存器必须被程序显式地访问。为了真正的8086模拟,您将在 虚拟-8086模式中运行 CPU,这些寄存器将不会被使用。

;

「 FS 」/「 GS 」登记册的用途是什么?

只需访问默认数据段(DS)之外的数据。


长读:

因此,我知道下列寄存器及其用途应该是什么:

[...]

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

ES 用于访问其他任何东西(* 2) ,超过 DS 的64KiB 的任何东西。像文字处理器的文本、电子表格的单元格或图形程序的图片数据等等。与通常假设的情况不同,这些数据的访问量不大,因此需要前缀的影响小于使用较长的地址字段。

类似地,在执行字符串运算操作时,DS 和 ES 可能不得不加载(并重新加载) ,这只是一个小麻烦——这至少被当时最好的字符处理指令集之一抵消了。

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

一开始,由于8位世界(* 3)的小程序和同样小的数据集,这没什么大不了的,但是很快就成了一个主要的性能瓶颈——对于程序员(和编译器)来说更是一个真正的麻烦。随着386英特尔最终提供的救济,增加了两个部分,所以任何系列 一元二进制三元操作,元素分散在内存中,可以发生在没有重新加载 ES 所有的时间。

对于编程(至少在汇编中)和编译器设计来说,这是一个相当大的收获。当然,本来可以有更多的,但是有了三个,瓶颈基本上消失了,所以没有必要做过头。

明智地命名字母 F/G 仅仅是 E 之后的字母顺序延续。至少从 CPU 设计的角度来看,没有什么是相关的。


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

* 2-所以事后看来,‘ Everything Else Segment’的命名方式要比‘ Ultra Segment’好得多。

* 3-要记住,8086只是在 8800完成之前的权宜之计,主要是为了让嵌入式世界保持8080/85客户。

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

当80386处理器在1985年推出时,在 MS-DOS 下使用640KB 内存的 PC 机很普遍。内存是昂贵的,而且个人电脑主要是在 MS-DOS 的实时模式下运行,最大的内存数量。

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

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

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