使用分段寄存器

问题描述 投票:6回答:3

我试图了解内存管理如何进行低级别的处理,并有几个问题。

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

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

3)不使用分段的操作系统如何将程序划分为逻辑段?例如,它们如何区分堆栈和没有段描述符的代码。我了解到分页可用于处理此类问题,但不了解如何处理。

linux memory x86 memory-segmentation
3个回答
3
投票

展开Benoit's answer to question 3 ...

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

首先,您的编译器(和链接器)将在指定此分区的位置创建可执行文件。如果查看多种可执行文件格式(PE,ELF等),您会发现它们支持某种类型的节或段或任何您想调用的格式。除了文件中的地址,大小和位置之外,这些部分还具有告诉操作系统这些部分目的的属性,例如这部分包含代码(这是入口点),这是-初始化的常量数据,-是未初始化的数据(通常不占用文件空间),这是关于堆栈的内容,在那上面是依赖项列表(例如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可执行文件格式非常简单。


4
投票
  1. 您必须已经读了一些真正的旧书,因为没有人再为real-mode编程;-)在real-mode中,您可以使用phyical address = segment register * 0x10 + offset获取内存访问的物理地址。 ,偏移量是通用寄存器之一内部的值。由于这些寄存器的宽度为16位,因此一个段的长度为64kb,您将无法对其大小做任何事情,因为没有属性!通过* 0x10乘法,可以使用1mb的内存,但是根据您在段寄存器地址寄存器中放置的内容,会有重叠的组合。我还没有为real-mode编译任何代码,但是我认为在二进制加载期间由OS来设置segment register就像由加载器在加载ELF时分配一些页面一样二进制但是,我确实已经编译了裸机内核代码,因此我必须自己设置这些寄存器。

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

  3. 我没有任何使用分段的严肃操作系统的示例,只是因为为了向后兼容,分段仍然存在。使用平面模型方法不过是摆脱它的一种手段。无论如何,您是对的,分页是一种更高效,更通用的方式,并且几乎在所有架构上都可用(至少在概念上)。我在这里无法解释分页内部结构,但是您需要了解的所有信息都在出色的Intel人员内部:Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1


-1
投票

在这种情况下,维基百科是您的朋友。 http://en.wikipedia.org/wiki/Memory_segmentationhttp://en.wikipedia.org/wiki/X86_memory_segmentation应该是很好的起点。

不过,我确定这里还有其他人可以亲自提供深入的解释。

© www.soinside.com 2019 - 2024. All rights reserved.