在 Linux 内核中,vmalloc_to_page 及相关的 page 系列函数用于处理内核虚拟地址与物理页的映射关系。这些函数在管理 vmalloc 分配的虚拟地址、访问物理页以及操作页表时非常重要。以下是对这些函数的详细解析。
vmalloc_to_page
vmalloc_to_page
是一个内核函数,用于将 vmalloc
分配的虚拟地址转换为其对应的物理页。
定义
struct page *vmalloc_to_page(const void *vmalloc_addr);
参数
vmalloc_addr
: 由 vmalloc
分配的虚拟地址。
返回值
对应的 struct page
结构,表示该虚拟地址对应的物理页。
工作原理
vmalloc
分配的虚拟地址是虚拟连续但物理上可能不连续的。
vmalloc_to_page
会遍历页表,找到虚拟地址对应的物理页。
示例
#include <linux/vmalloc.h>
#include <linux/mm.h>
void example_vmalloc_to_page(void)
{
void *buffer = vmalloc(1024 * 1024); // 分配 1 MB 虚拟连续内存
if (!buffer) {
printk(KERN_ERR "vmalloc failed\n");
return;
}
// 将 buffer 的一个虚拟地址转换为物理页
struct page *page = vmalloc_to_page(buffer);
if (page) {
printk(KERN_INFO "Physical page frame number: %lu\n", page_to_pfn(page));
}
vfree(buffer); // 释放内存
}
virt_to_page
virt_to_page
将内核直接映射的虚拟地址转换为对应的物理页。
定义
struct page *virt_to_page(const void *kaddr);
参数
kaddr
: 内核直接映射的虚拟地址。
返回值
对应的 struct page
结构。
限制
只能用于内核直接映射区域(即物理地址和虚拟地址之间简单偏移的区域,例如 kmalloc
分配的内存)。
不能用于 vmalloc
分配的地址,因为 vmalloc
使用的是虚拟连续地址,映射方式不同。
示例
void example_virt_to_page(void)
{
void *buffer = kmalloc(4096, GFP_KERNEL); // 分配 4 KB 内存
if (!buffer) {
printk(KERN_ERR "kmalloc failed\n");
return;
}
struct page *page = virt_to_page(buffer);
printk(KERN_INFO "Page frame number: %lu\n", page_to_pfn(page));
kfree(buffer); // 释放内存
}
pfn_to_page
pfn_to_page
将物理页帧号PFN
转换为 struct page
。
定义
struct page *pfn_to_page(unsigned long pfn);
参数
pfn
: 物理页帧号。
返回值
对应的 struct page
结构。
示例
void example_pfn_to_page(void)
{
unsigned long pfn = 0x12345; // 示例页帧号
struct page *page = pfn_to_page(pfn);
printk(KERN_INFO "Page address: %p\n", page);
}
page_to_pfn
page_to_pfn
将 struct page
转换为物理页帧号。
定义
unsigned long page_to_pfn(const struct page *page);
参数
page
: struct page
结构。
返回值
物理页帧号。
示例
void example_page_to_pfn(void)
{
struct page *page = alloc_page(GFP_KERNEL); // 分配一个页
if (!page) {
printk(KERN_ERR "alloc_page failed\n");
return;
}
unsigned long pfn = page_to_pfn(page);
printk(KERN_INFO "Physical frame number: %lu\n", pfn);
__free_page(page); // 释放页
}
page_address
page_address
将 struct page
转换为内核直接映射的虚拟地址。
定义
void *page_address(const struct page *page);
参数
page
: struct page
结构。
返回值
内核直接映射的虚拟地址。
限制
只能用于内核直接映射区域,不适用于 vmalloc
地址。
示例
void example_page_address(void)
{
struct page *page = alloc_page(GFP_KERNEL); // 分配一个页
if (!page) {
printk(KERN_ERR "alloc_page failed\n");
return;
}
void *addr = page_address(page);
printk(KERN_INFO "Direct-mapped virtual address: %p\n", addr);
__free_page(page); // 释放页
}
函数适用场景对比
函数名 | 输入 | 输出 | 适用场景 |
---|---|---|---|
vmalloc_to_page |
vmalloc 分配的虚拟地址 |
对应的物理页 (struct page ) |
用于访问 vmalloc 地址对应的物理页 |
virt_to_page |
内核直接映射的虚拟地址 | 对应的物理页 (struct page ) |
用于 kmalloc 等内存分配的地址 |
pfn_to_page |
物理页帧号 (PFN ) |
对应的物理页 (struct page ) |
从 PFN 转换到 struct page |
page_to_pfn |
struct page |
对应的物理页帧号 | 从 struct page 获取物理地址 |
page_address |
struct page |
内核直接映射的虚拟地址 | 获取页的直接映射虚拟地址 |
总结
vmalloc_to_page
是专门为 vmalloc
地址设计的,可以获取虚拟地址对应的物理页。
virt_to_page
和 page_address
主要用于直接映射区域(如 kmalloc
分配的内存)。
pfn_to_page
和 page_to_pfn
提供页帧号与 struct page
的相互转换。
根据具体内存分配方式和需求,选择合适的函数操作页表或物理页结构。