16位MS-DOS内存模型回顾

2020-08-11 03:47:26

在MS-DOS和16位Windows编程中,您必须处理内存模型。这个术语不是指处理器体系结构内存模型(处理器如何与内存交互),而是指程序如何在内部组织自身。操作系统本身对应用程序内存模型一无所知;它只是讨论程序如何处理不同类型的代码和数据的一种便捷方式。

内存模型的术语来自C编译器,因为这会通知编译器要生成哪种类型的代码。四种基本型号可以放进一张漂亮的桌子里:

8086使用分段存储器,这意味着指针由两部分组成:段和偏移量。远指针由段和偏移量组成。近指针仅由偏移量组成,并隐含了段。

一旦您拥有超过64KB的代码或超过64KB的数据,您就必须(分别)切换到远代码指针或远数据指针,以便引用所需的所有内容。

我的大多数程序都是紧凑的,这意味着代码不是很多,但是有很多数据。这是因为我编写的程序倾向于进行大量的数据处理。

介质模型对于具有大量代码但数据不多的程序非常有用。用户界面代码通常属于这一类,因为您必须编写大量代码来管理对话框,但所有这些工作的结果只是一个文本字符串(来自编辑框)和一些标志(来自某些复选框)。很多电脑游戏也属于这一类,因为你有很多的游戏逻辑,但是没有太多的游戏状态。

MS-DOS有一个额外的内存模型,称为微型机,其中代码和数据都被合并到一个64KB的段中。这是以.com扩展名结尾的程序所需的内存模型,它的存在是为了向后兼容CP/M。CP/M在支持最大64KB内存的8080处理器上运行。

远指针可以访问8086的1MB地址空间中的任何内存,但每个对象仍然被限制在64KB,因为指针运算只在指针的偏移部分执行。每当偏移量溢出时,通过调整段,大指针可以引用大于64KB的内存块。²使用大指针的指针算术计算开销很大,因此您不会经常使用它们。²

您并不局限于上述内存模型。您可以自己编写,称为混合模型编程。例如,您可以说您的程序的大部分是小内存模型,但是有一个地方您需要访问默认数据段之外的内存,因此您为此声明了一个显式的远指针。类似地,您可以定义一个显式的Far函数来将其移出默认代码段。

内存模型指定了默认行为,因此如果调用(比方说)memcpy,就会得到一个指针大小与内存模型匹配的版本。如果您有一个小型或中型的程序,并且想要复制默认数据段之外的内存,您可以调用_fmemcpy函数,该函数与memcpy相同,只是它需要较远的指针。(如果您使用的是紧凑型或大内存模型,则memcpy和_fmemcpy是相同的。)。

我的一位前同事回到了学校,正在和他(年轻的)导师交谈。不知何故,话题转向了8086处理器。我的同事和他的朋友解释了分段寻址、近指针和远指针、指针相等和比较的行为方式、神秘的A20门,以及实模式引导扇区是如何工作的。“他对分部寄存器过去的工作方式感到恐惧的表情是无价的。”

我很快纠正了我的同事。“‘以前工作过’?他们还在这样做呢!“。

额外的优势:您可以在windows.h定义的近宏和远宏中看到16位内存模型的残留物。它们不再有任何意义,但它们仍然是为了源代码向后兼容。

?在MS-DOS中,通过将20位地址的高16位放入段中并将剩余的4位放入偏移量来操作巨大的指针。MS-DOS中巨型指针的偏移量始终小于16。

²在16位Windows中,有一个名为hmemcpy的系统函数,它复制可能大于64KB的内存块。现在你知道h前缀代表什么了。

³分段内存是反例的一个很好的来源。例如,在分段内存模型中,可以有一个指向大小为N的缓冲区的指针p,而另一个指针q可以满足不等式

这就是为什么C和C++编程语言不指定不指向同一数组元素的指针之间的比较结果的原因之一。