在 x86 平台上运行的 Linux 中,在受保护的内核模式下,实模式地址空间映射到哪里?在内核模式下,线程可以直接访问内核地址空间。内核在较低的8MB,页表在某个位置,等等(如此处所述(。但是真正的模式地址空间去哪儿了?可以直接访问吗?例如 BIOS 和 BIOS 插件(见这里(?
(我的x86-fu有点弱。我会添加一些标签,以便其他人可以(希望(纠正我,如果我在任何地方撒谎。
物理地址在实模式和保护模式下是相同的。唯一的区别在于如何从指令中指定的地址(偏移量(获取到物理地址:
-
在实模式下,物理地址基本上是
(segment_reg << 4) + offset
。 -
在保护模式下,物理地址为
translate_via_page_table([segment_reg] + offset)
。
[segment_reg]
我的意思是段的基址,在全局或本地描述符表中查找的偏移量为 segment_reg
. translate_via_page_table()
表示通过分页(如果启用(完成的地址转换。
看这里,似乎BIOS ROM出现在物理地址0x000F0000-0x000FFFFF。若要在保护模式下使用分页获取该内存,必须通过设置正确的页表条目将其映射到虚拟地址空间的某个位置。假设 4 KB 页(通常情况(,映射整个范围应需要 16 ((0xFFFFF-0xF0000+1(/4096( 个条目。
要了解 Linux 内核是如何工作的,您可以研究如何例如 /dev/mem
,它允许读取任意物理地址,被实现。实现在 drivers/char/mem.c 中。
以下命令(例如此答案(将转储内存范围0xC0000-0xFFFFF(这意味着根据上面链接的内存映射,它也包括视频 BIOS(:
$ dd if=/dev/mem bs=1k skip=768 count=256 > bios
1024*768 = 0xC0000,1024*(768+256( - 1 = 0xFFFFF,给出预期的物理内存范围。
稍微跟踪一下,read_mem()
在 drivers/char/mem.c 调用 xlate_dev_mem_ptr()
中,它在 arch/x86/mm/ioremap.c 中有一个特定于 x86 的实现。如果需要,该函数中的ioremap_cache()
调用似乎负责页面中的映射。
请注意,顺便说一下,BIOS 例程在保护模式下不起作用。他们假设 CPU 以实模式运行。
对于 Linux x86 32 位,物理 RAM 的第一896MB
映射到从虚拟地址0xC0000000
开始的连续虚拟内存块到0xF7FFFFFF
。从0xF8000000
到0xFFFFFFFF
的虚拟地址动态分配给物理内存的各个部分,因此内核可以将128MB
窗口映射到超出896MB
限制的物理内存的任何部分。
内核本身在 1MB 及以上的物理地址加载,留下第一个 MB 可用。例如,第一个 MB 用于具有 ISA 设备需要存在的 DMA 缓冲区,因为它们使用 8237 DMA 控制器,该控制器只能映射到此类地址。
因此,从虚拟内存地址读取0xC0000000
实际上是从物理地址0x00000000
读取(前提是内核已将该页面标记为存在(