- 物理内存:真实的内存芯片颗粒,在物理总线上占据一段或几段连续的地址范围。
- 虚拟内存:用户程序使用虚拟内存地址访问内存单元,虚拟内存地址总是要翻译成物理总线上的地址。这样一来,物理总线上不连续的内存范围,在虚拟内存地址上也可能是连续的,这满足了用户程序对大块连续内存的使用需求。
在已经运行过一段时间的系统中,分配一个物理地址上连续的、大内存是比较困难的;Linux操作系统的swiotlb会在系统启动早期预留出一块64MB的连续物理内存,用于满足DMA内存请求。
从物理总线上看,地址连续的大内存很难满足;但是从虚拟内存地址上,这可以通过MMU做到。处理器CPU上的每个核都有一个MMU,以及现代GPU也有一个MMU(我们称之为“IOMMU”)。从物理内存来看:
- CPU上的每个核和GPU都是主设备,都可以发起对物理内存的访问,物理内存都会响应这种访问;
- CPU上的每个核和GPU使用各自的虚拟地址访问物理内存时,经过各自的MMU地址翻译后,最终应该时物理内存所在的物理总线上的地址。
CPU使用页表来记录虚拟地址到物理地址的映射关系;GPU也使用类似的机制(我们称之为GART表)。
CPU和GPU的交互要确保:
- CPU写的数据能被GPU看到;
- GPU写的数据能被CPU看到;
由于存在缓存和写缓冲的机制,CPU访问内存一般有三种属性
- UC,无缓存、无写合并(CPU访问内存的效率最差)
- WC,无缓存、有写合并
- WB,有缓存、有写合并(CPU访问内存的效率最高)
CPU对物理内存的写,一般是无确认写。因此,当CPU完成对内存写之后,为了确保GPU能看到,可能需要:
- 内存屏障操作
- 缓存同步操作
- dummy IO读操作
而GPU在写物理内存之后,要确保CPU能看到,CPU可能需要
- 缓存同步操作
- GPU提供的硬件锁同步操作