ChatGPT解决这个技术问题 Extra ChatGPT

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

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

CS = 代码段(用于 IP)

DS = 数据段(用于 MOV)

ES = Destination Segment(用于MOVS等)

SS = 堆栈段(用于 SP)

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

FS =“文件段”?

GS = ???

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

据我所知,这两个中的 F 和 G 不代表任何东西。只是 CPU 上(和指令集中)有六个用户可指定的段寄存器的空间,有人注意到除了“S”tack 段之外,字母“C”和“D”(代码和数据)是按顺序排列的,所以“E”是“额外”部分,然后是“F”和“G”。
本来可以,除非你当时在场,否则总是很难知道别人脑子里在想什么(我当时在另一个海岸,离英特尔的设计团队很远)。
想想我们可以用 BS register 获得多少乐趣 :-}
我一直使用 GS 作为“图形段”。 :-)
“G”一般“S”段怎么样?

p
peterh

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

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

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

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

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

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

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


@supercat:一个更简单、更出色的方案可以让它们寻址 65536 倍的存储空间,将段寄存器视为低 16 位的完整高 16 位扩展,本质上就是 286、386 Multics做到了。
@IraBaxter:这种方法的问题在于,80286 样式的段具有足够高的开销,而不是最终必须在每个段中存储许多对象,因此在每个指针上都存储段和偏移量。相比之下,如果愿意将内存分配四舍五入到 16 字节的倍数,则 8086 样式的分段允许单独使用段作为识别对象的一种方式。在 1980 年将分配舍入到 16 个字节可能有点令人讨厌,但如果它将每个对象引用的大小从 8 个字节减少到 4 个,那么今天将代表一个胜利。
这些寄存器用于现代操作系统。它们主要用于指向有关任务控制块的信息,至少在现在可用于 x86 芯片的两个主要操作系统中。而且,由于它们不再是“通用目的”,即使它们的初衷也是如此,因此您不能大量使用它们。最好在 x86-64 系统上假装它们根本不存在,直到您需要它们允许您在线程控制块中访问的信息。
基于过时的科学,附录类比真的很糟糕;它与免疫系统有关,所以绝对不是“退化的”。它有损于实际职位。除此之外,这是一个很好的回应。
感谢您对分段内存和平面内存进行有趣的、无拘无束的处理 :) 在 6809(有和没有分页内存)、6502、z80、68k 和 80[123]?86 上编写了代码,我的观点是分段的记忆是一场恐怖表演,我很高兴它被扔进了历史的垃圾箱。使用 FS 和 GS 来有效访问 thread_local 数据是历史错误的意外结果。
J
Johan

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


它们是打算用于操作系统定义的目的,还是为了方便需要执行类似 *dest++ = lookup[*src++]; 的代码,否则如果 dest、lookup 和 src 位于三个不相关的位置,这将是相当尴尬的。
在 Windows FS 上确实是用于线程特定的存储。在此处查看 FS 指向的块的记录地图 en.wikipedia.org/wiki/Win32_Thread_Information_Block
它不仅在 Windows 上。 GS 还用于 OS X 上的 TLS。64 位内核也使用 GS 来跟踪上下文切换期间的系统结构。操作系统将为此使用 SWAPGS。
“在 Windows 中,GS 寄存器用于管理线程特定的内存”......不是 FS 吗?
@tuket 他们的 32 位操作系统使用 fs,他们的 64 位操作系统使用 gs。 linux做了相反的动作。
z
zerocool

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

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

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

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

这实际上并没有回答这个问题。问题说明注意:我不是在询问任何特定的操作系统——我是在询问它们打算被 CPU 用于什么,如果有的话。
@MichaelPetch 是的,我知道我只是想将此添加为那些在 SO 中阅读此 q/s 的人的好信息
P
Peter Cordes

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 英特尔终于通过添加两个段来减轻压力,因此任何系列 unarybinaryternary 操作,元素分布在内存中,都可以在不重新加载 ES 的情况下进行。

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

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

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

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

*3 - 请务必牢记,8086 仅用作 8800 完成之前的权宜之计,主要用于嵌入式世界以保持 8080/85 客户的参与。


哇,谢谢你解释这一切!这解释了很多,也很有意义! +1
R
Robert Houghton

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

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

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

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


M
Maxim Masiutin

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 has already replied 这些寄存器在处理分散在内存中的元素时很有用,以避免一直重新加载其他段寄存器,如 ES。但我想强调的是,这仅与实模式或 16 位保护模式下的 64KB 段有关。

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

80286 处理器具有 16 位保护模式,但没有 FS/GS 寄存器。所以程序在使用这些寄存器之前必须先检查它是否在 80386 下运行,即使是在真正的 16 位模式下也是如此。请参阅example of use of FS and GS registers a program for MS-DOS real mode