我对 linux 如何管理 I/O 共享内存以与使用它的设备进行通信感到非常困惑。
如果我理解正确的话,linux 内核会从 0x100000 开始映射(以避免第一个兆字节的遗留 ram 数据并存储在连续的内存位置),然后进入保护模式后:
- 在 32 位系统上有这样的映射
ZONE_NORMAL 应低于 896 MB,因此内核线性 1GB 与物理 896 MB 之间的映射始终是可能的。现在让我们忽略 ZONE_DMA(我读到这仅适用于旧系统,因为 PCI 现在可以在内存中的任何地方使用 DMA 传输)
- 在 64 位系统上,内核线性地址空间应从 PAGE_OFFSET= 0xffff810000000000 开始,依次类推
在这两种情况下,如果内核空间中的地址大于 PAGE_OFFSET,则应引用 ioremap 映射(通过分页解析),如果它低于 PAGE_OFFSET,则可以使用简单的 NEW_ADDRESS = OLD_ADDRESS - PAGE_OFFSET 来解析。它是否正确?
额外问题:当内核启动并运行并且整理完成后,它是否仍然物理地驻留在 0x100000 前后(在第一个 GB 内)?即使在 64 位系统上?
答案1
在 PC 上,硬件内存映射 IO 范围由 BIOS 分配给 3GiB 到 4 GiB 之间的物理内存地址。当驱动程序请求访问内存时,内核会将其映射到内核虚拟地址空间中的某个位置。
您的其他两个问题似乎都与共享内存无关,但是:
在这两种情况下,如果内核空间中的地址大于 PAGE_OFFSET,则应引用 ioremap 映射(通过分页解析),如果它低于 PAGE_OFFSET,则可以使用简单的 NEW_ADDRESS = OLD_ADDRESS - PAGE_OFFSET 来解析。它是否正确?
从精神上来说,是的。无论哪种情况,硬件都会使用页表。
答案2
认为你的问题看起来像,ioremap 是如何工作的..
vaddr = ioremap(paddr_io_mapped_device, 大小);
vaddr 是返回的内核空间中的虚拟地址。内核为虚拟地址范围( vaddr ,大小)创建页表条目,并将其映射到物理地址 paddr_io_shared_device 。因此,如果访问虚拟地址范围,则与访问 io_mapped 设备内的物理地址一样好。
重要的是,返回的 vaddr 是不可缓存。每次读/写地址范围时,都会从 io_mapped 设备而不是从缓存读/写。