分段寄存器使用

Ant*_*lov 6 linux memory x86

我试图了解内存管理如何进入低级别并且有几个问题.

1)由Kip R. Irvine撰写的关于汇编语言的书说,在实模式中,前三个段寄存器在程序启动时加载了代码,数据和堆栈段的基址.这对我来说有点不合时宜.是手动指定这些值还是汇编程序生成将值写入寄存器的指令?如果它自动发生,它如何找出这些段的大小?

2)我知道Linux使用扁平线性模型,即以非常有限的方式使用分段.此外,根据Daniel P. Bovet和Marco Cesati的"理解Linux内核",有四个主要部分:用户数据,用户代码,内核数据和GDT中的内核代码.所有四个段都具有相同的大小和基址.我不明白为什么四个中只有它们的类型和访问权限不同(它们都产生相同的线性地址,对吧?).为什么不只使用其中一个并将其描述符写入所有段寄存器?

3)不使用分段的操作系统如何将程序划分为逻辑段?例如,他们如何在没有段描述符的情况下将堆栈与代码区分开来.我读过分页可以用来处理这些事情,但是不明白怎么做.

Ben*_*oit 5

    \n
  1. 您一定读过一些非常老的书,因为没有人再为实模式编程;-) 在实模式中,您可以使用 获取内存访问的物理地址physical address = segment register * 0x10 + offset,偏移量是通用寄存器之一内的值。因为这些寄存器是 16 位宽,所以一个段的长度为 64kb,并且您无法对其大小进行任何操作,因为没有属性!通过* 0x10乘法,1mb 的内存变得可用,但是根据您在段寄存器地址寄存器中放入的内容,存在重叠组合。我还没有为实模式编译任何代码,但我认为操作系统在二进制加载期间设置段寄存器,就像加载程序在加载 ELF 二进制文件时分配一些页面一样。然而,我确实编译了裸机内核代码,并且我必须自己设置这些寄存器。

    \n
  2. \n
  3. 由于架构限制,扁平模型中必须有四个部分。在保护模式下,段寄存器不再包含段基地址,而是包含一个段选择器,它基本上是 GDT 的偏移量。根据段选择器的值,CPU将处于给定的特权级别,这就是CPL(当前特权级别)。段选择器指向一个具有 DPL(描述符特权级别)的段描述符,如果段寄存器充满了该选择器,则最终是 CPL(至少对于代码段选择器来说是这样)。因此,您至少需要一对段选择器来区分内核和用户区。此外,段要么是代码段,要么是数据段,因此最终会在 GDT 中得到四个段描述符。

    \n
  4. \n
  5. 我没有任何使用分段的严肃操作系统的示例,只是因为分段仍然存在以实现向后兼容。使用平面模型方法只不过是摆脱它的一种手段。不管怎样,你是对的,分页更加高效和通用,并且几乎可以在所有架构上使用(至少在概念上)。我无法在这里解释分页内部原理,但您需要了解的所有信息都在优秀的 Intel man 中:Intel\xc2\xae 64 和 IA-32 架构\n软件开发人员\xe2\x80\x99s 手册\n第 3A 卷: \n系统编程指南,第 1 部分

    \n
  6. \n
\n

  • “不再有人使用实模式程序”是夸大其词。:) (2认同)
  • “线程本地存储”是 x86 段在现代操作系统中仍然使用的一个例子,尽管几乎所有其他东西都使用“平面内存模型”。 (2认同)

Ale*_*nze 4

扩展伯努瓦对问题 3 的回答......

将程序划分为代码、常量数据、可修改数据和堆栈等逻辑部分是由不同的代理在不同的时间点完成的。

首先,编译器(和链接器)在指定此划分的位置创建可执行文件。如果您查看多种可执行文件格式(PE、ELF 等),您会发现它们支持某种类型的节或段或任何您想要的名称。除了文件中的地址、大小和位置之外,这些部分还具有告诉操作系统这些部分的用途的属性,例如,该部分包含代码(这是入口点),this - 初始化的常量数据,that - 未初始化的数据(通常不采用文件中的空间),这里是有关堆栈的信息,那边是依赖项列表(例如 DLL)等。

接下来,当操作系统开始执行程序时,它会解析文件以查看程序需要多少内存,每个部分需要在哪里以及需要哪些内存保护。后者通常通过页表完成。代码页被标记为可执行和只读,常量数据页被标记为不可执行和只读,其他数据页(包括堆栈的数据页)被标记为不可可执行和读写。正常情况下应该是这样的。

通常,程序需要可读写的区域,同时还需要可执行区域来动态生成代码,或者只是为了能够修改现有代码。组合的 RWX 访问可以在可执行文件中指定,也可以在运行时请求。

还可以有其他特殊页面,例如用于动态堆栈扩展的保护页面,它们放置在堆栈页面旁边。例如,您的程序开始时为 64KB 堆栈分配了足够的页面,然后当程序尝试访问超出该点时,操作系统会拦截对这些保护页面的访问,为堆栈分配更多页面(最多支持的最大大小)并进一步移动保护页。这些页面不需要在可执行文件中指定,操作系统可以自行处理它们。该文件应该只指定堆栈大小以及位置。

如果操作系统中没有硬件或代码来区分代码内存和数据内存或强制执行内存访问权限,则这种划分是非常正式的。16 位实模式 DOS 程序(COM 和 EXE)没有以某种特殊方式标记的代码、数据和堆栈段。COM 程序将所有内容都集中在一个公共的 64KB 段中,它们以 IP=0x100 和 SP=0xFFxx 开始,内部代码和数据的顺序可以是任意的,它们几乎可以自由地交织在一起。DOS EXE 文件仅指定起始 CS:IP 和 SS:SP 位置,除此之外,代码、数据和堆栈段与 DOS 无法区分。它所需要做的就是加载文件、执行重定位(仅适用于 EXE)、设置 PSP(程序段前缀,包含命令行参数和一些其他控制信息)、加载 SS:SP 和 CS:IP。它不能保护内存,因为内存保护在实地址模式下不可用,因此16位DOS可执行文件格式非常简单。