DOS 内存粗解


类型:LinuxApp,创建时间:Oct. 18, 2022, 11:32 p.m.

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/114/。

今天搞明白了,为啥 dos 有个 A20 地址线,以及 DOS 所谓高位内存的技术了。原因出在那个段地址的计算上面,经常看到的段的计算方式,实际上不止可以访问到 1M 的内存,而是 1M 再多一些:

0xffff * 16 + 0xffff

可以访问到比 1M 高一点的 64k 内存。实际上不是 64k,而是 64k-16 字节。因为 ffff:0010 就是 1M

比较早的 DOS 程序,会利用 8086 的 bug,访问这段内存实际上是访问0000:0000这一段内存。后来 80286 出现,这个 bug 就失效了。

由此引申出 DOS 内存的几个概念。

DOS 内存分区

Conventional memory

应用程序最常用的 0K 到 640K 内存段。

Upper memory area

简称 UMA,不过有时候也被称为 upper memory blocks (UMBs).

In DOS memory management, the upper memory area (UMA) refers to memory between the addresses of 640 KB and 1024 KB (0xA0000–0xFFFFF) in an IBM PC or compatible. IBM reserved the uppermost 384 KB of the 8088 CPU's 1024 KB address space for BIOS ROM, Video BIOS, Option ROMs, video RAM, RAM on peripherals, memory-mapped I/O, and obsoleted ROM BASIC.[1]

以上摘录自 wiki,640k 限制的存在,是因为 IBM 保留那 640K 到 1024K 这个内存段,其实并没有存在真正的硬件限制。高版本的 DOS 可以把一些程序挪到这个位置。不过需要注意的是,这些内存段实际上已经被各种 BIOS 的程序给使用了,并不是空的内存空间。通过在fdconfig.sys里面设置:

DEVICEHIGH=xxx

可以让 DOS 把驱动程序挪到 UMA 区域未被使用的段空间里面。

怎么做到的呢?通过 80386 的保护模式,把一个个的段映射到 1M 以上的空间就行了。 不这需要注意的是,已经被 BIOS 的使用的段,比如显存,仍然是显存,段空间是不能被使用的。

这一个空间很重要,是因为这一段空间是可被 MSDOS 程序直接寻址的位置。所谓的直接写屏,就是直接写 UMA 里面的显存。各种 TSR 程序,为了不占用前 640K 内存,一般都会被加载到这一段内存空间来。

FreeDOS 在安全模式下的内存布局:

Memory Type             Total          Used           Free
----------------------------------------------------------
Conventional            638K           73K            565K
Upper                   0K             0K             0K
Reserved                386K           386K           0K
Extended                15,232K        157K           15075K

此时 FreeDOS 加载了himem.sys,但是没有使用 UMA 内存。而加载了EMM386.sys以后,内存布局变成了:

Memory Type             Total          Used           Free
----------------------------------------------------------
Conventional            638K           11K            627K
Upper                   76K            27K            49K
Reserved                310K           310K           0K
Extended                15,232K        857K           14723K

可以看到此时 Extended 被多使用了一些,并且 Upper 内存空间被找出来 76K,并且使用了 27K,主要是 FreeDOS 自带的组件。如果还有其它 TSR 的话,也可以加载到这个 UMA 里面。

根据 wikipedia 的说法:

https://en.wikipedia.org/wiki/EMM386

FreeDOS 的 JEMM386 没有实现由微软定义的 Global EMM Import Specification (GEMMIS),所以不支持 win3.x 的运行。不过 dosbox 支持了,所以可以在 dosbox 里面运行 win3.x

High memory area

这个内存段就是我前面所说的那一段在 1M 内存之上的 64K-16 字节。在config.sys里面写上

DOS=high

就会把 MSDOS 的一些代码加载到这里来。各种类型的 DOS 都会使用这一空间。而且 MSDOS 的某些特殊程序也可以和 MSDOS 共享这一空间。要打开这个功能,同样需要 himem.sys 这个驱动。

正常 IBM PC 机会把这一空间解释为 0000:0000 这个段,除非打开 A20 Gate. 有时候在 BIOS 里面会看到这个配置项。

Extended memory

1M 以上的内存空间。这段空间正常是没法使用的,但是 80386 引入了保护模式,微软发布了一个范围,使用 band switch 的技术来使用这一段内存,称为 Expanded Memory Specification (EMS). 其基本原因是把 1M 以下的内存地址,映射到 1M 以上。msdos 的实现是使用 UMA 地址,但其它公司发现还可以使用 Conventional 地址,这导致了在 DOS 下出现所谓的多任务软件,因为,只要把正在运行的任务的内存给切换走,CPU 根本不知道正在执行着多任务。

可以使用硬件来实现 bank switch,但是后来大多数情况下都被软件模拟给代替了。在 msdos 下,使用 himem.sys 来打开这个功能。

Expanded Memory Adapter (XMA)

这个技术是 IBM 搞出来的,用于突破 1M 内存限制,但实际上没啥人使用。

保护模式下的 DOS 内存管理,以及 DOS extender

以上说的那些技术,都是为了让传统的 8086 16bit DOS 程序良好运行用的。但新的 80286 以及 80386 的 DOS 程序,运行在保护模式下,可直接访问系统的所有内存。但此时 DOS 的所有系统调用都没法用了,因为段地址和 DOS 定义的那些段地址不兼容,比如 80286 底下的段地址,高 13 位用于指向 GDT 或者 LDT,第 14 位用于指示 GDT 或者 LDT,而最后两位则是权限级别,80386 则是线性的空间。如此这般就需要一所谓的 DOS Protected Mode Interface (DPMI),给这些运行在保护模式下的 dos 程序提供 DOS 接口。最常见的就是 DOS/4GW 了,它的开源替代者是 DOS/32。Windows 本身也是 DOS Extender. DJGPP 也包含了一个 CWSDPMI. 实际上这些 DOS Extender 就是操作系统。

https://en.wikipedia.org/wiki/DOS_extender

DOS Protected Mode Services

当 TSR 或者驱动运行在保护模式,向 16bit DOS 实模式提供服务的办法。貌似只有 Novell 和 PC DOS 提供这个功能。可能是 Novell 操作系统的功能吧。

关于 80286 的保护模式

在 80286 的保护模式下,段地址被分成三部分,高 13 位用于指向 GDT 或者 LDT,第 14 位用于指示 GDT 或者 LDT,而最后两位则是权限级别。为啥是从高处开始分呢?因为理论上后 4 位压根是没用的,在 seg * 16 的情况下,连续的 16 个 seg 其实是重叠的。要做成不重叠的,只要用掉 seg 的前 12 位就行了。但 intel 这里又多加了 1 位。所以实际上可以做到以 32k 为单位来管理内存。第 14 位的 LDT 如果也用上的话,理论上可以做到 16k 为单位来管理内存吧?

unreal 模式

80286 以后的 CPU 都采用了 GDT 和 LDT 来管理内存。只要先切换到 80286 保护模式,修改完 GDT,再切换回 real 模式,这里系统就会进入 unreal 模式。此时系统理论上可以访问很大的内存空间。不过这个模式和 virtual 8086 模式不兼容。似乎并没有太多软件用这个办法。

https://wiki.osdev.org/Unreal_Mode

顺便看看 DOS 的 API 列表

https://en.wikipedia.org/wiki/DOS_API

目前开源社区经常使用的 DJGPP 是一个 32 位的编译器,生成 32 位的代码在 80386 以后运行。但是它碰到读写文件,会切换到 DOS 16 位调用。这就是所谓的 DPMI,但这只是接口标准,具体的实现,在各种 DPMI 服务中实现。比如 win 3.x,dos/4g 等等。它们也叫 dos extender,虽然未必实现 DPMI 接口,但后来基本上都实现 DMPI 接口去了。上文有介绍。

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/114/。


暂时还没有任何评论。


何不来发表一下您对本文的看法(使用Markdown语法,分段空两行):