![为什么物理地址对齐 4KiB 时,虚拟地址却不是 4KiB 对齐?](https://linux22.com/image/226833/%E4%B8%BA%E4%BB%80%E4%B9%88%E7%89%A9%E7%90%86%E5%9C%B0%E5%9D%80%E5%AF%B9%E9%BD%90%204KiB%20%E6%97%B6%EF%BC%8C%E8%99%9A%E6%8B%9F%E5%9C%B0%E5%9D%80%E5%8D%B4%E4%B8%8D%E6%98%AF%204KiB%20%E5%AF%B9%E9%BD%90%EF%BC%9F.png)
简而言之,我有一个实际地址内核内部(9,932,111,872 或0x250000000
),这显然是对齐到 4KiB(页面大小)。当我使用核__va()
函数来获取内核虚拟地址,我得到类似的东西0xf570660f
(每次启动时都不同),即未与 4KiB 对齐。
我使用的是 64 位系统,因此没有 HIGHMEM,并且我认为由于线性内存模型,4KiB 对齐的物理地址的虚拟地址也应该是 4KiB 对齐的。我错过了什么?虚拟地址不应该是吗phys_addr + PAGE_OFFSET
?或者是sparsemem的影响?但也许它也应该是 4KiB 对齐的?
以下是更多详细信息:
我的工作环境是在 x86 64 位 QEMU VM 上。我正在尝试在模式下使用 PMEMDEV-DAX
作为普通内存。我可以得到它的物理起始地址(0x250000000
),已经确认是正确的。然后我需要将它转移到内核空间中的虚拟地址,以便我可以根据需要使用它。这是一些代码:
static long nvpc_map_whole_dev(struct dax_device *dax_dev, void **kaddr, pfn_t *pfn)
{
// get the device
struct dev_dax_nvpc *dax_nvpc = (struct dev_dax_nvpc *)dax_get_private(dax_dev);
// get the virtual address and the pfn_t
*kaddr = __va(dax_nvpc->phys_start);
*pfn = phys_to_pfn_t(dax_nvpc->phys_start, PFN_MAP);
pr_info("[NVPC DEBUG]: paddr %#llx kaddr %p pfn %lu\n", dax_nvpc->phys_start, *kaddr, pfn_t_to_pfn(*pfn));
pr_info("[NVPC DEBUG]: kaddr-paddr %#llx\n", __pa(*kaddr));
return PHYS_PFN(dax_nvpc->size);
}
这是我得到的结果:
如图所示, 、paddr
dax_nvpc->phys_start
、pfn
、 都是正确的。但kaddr
(虚拟地址)让我感到困惑。然后,当我将其传输kaddr
回物理地址(下一个输出行)时,结果是正确的。
更重要的是,我可以对内存从kaddr
到进行任何操作kaddr + dax_nvpc->size
,没有页面错误。
谁能告诉我为什么虚拟地址不是 4KiB 对齐的?我在某个地方是不是傻瓜?此外,我可以做些什么来确保虚拟地址也与页面对齐吗?
答案1
原因%p
在于printk
.当我更改%p
为%#llx
进行快速检查时,内核地址的输出按预期变为 4KB 对齐。
原因可以在这里找到:内核文档:printk 格式指针类型。%p
printk 内部将打印散列指针地址以防止泄漏内核信息。这就是为什么指针看起来很奇怪。如果你想检查真实的虚拟地址,只需使用%px
, 或添加no_hash_pointers
到启动参数中。更多使用方法请参考链接。