先上代码,主要用到virt_to_phys,虚拟物理地址的转换方法。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
static char *ptr;
int alloc_size = 1024;
struct stu
{
int a;
char *name;
unsigned int size;
unsigned int bb;
};
module_param(alloc_size, int, 0);
static int test_hello_init(void)
{
struct stu *aa;
ptr = kmalloc(alloc_size,GFP_ATOMIC);
aa = kmalloc(alloc_size,GFP_ATOMIC);
aa->a = 4;
aa->name="zhang";
aa->size=7;
aa->bb=10;
pr_err("memory_debug2 sself=%p, s=%p, %p, namepo%p %s, ssize:%u,%p, bbpointer:%p\n", &aa, aa, aa->name,&(aa->name), aa->name, aa->size, &(aa->size), &(aa->bb));
pr_err("memory_debug3 s,name,size,bb:%llx,%llx,%llx,%llx\n", (unsigned long long)aa, (unsigned long long)(&(aa->name)), (unsigned long long)(&(aa->size)), (unsigned long long)(&(aa->bb)));
if(!ptr) {
/* handle error */
pr_err("memory allocation failed\n");
return -ENOMEM;
} else {
pr_info("Memory allocated successfully:%p\t%p\n", ptr, ptr+100);
pr_info("Physical address:%llx\t %llx\n", virt_to_phys(ptr), virt_to_phys(ptr+100));
}
kfree(aa);
return 0;
}
static void test_hello_exit(void)
{
kfree(ptr);
pr_info("Memory freed\n");
}
module_init(test_hello_init);
module_exit(test_hello_exit);
MODULE_LICENSE("GPL");
[ 90.569094] <1>.(1)[7809:insmod]calling test_hello_init+0x0/0x184 [mem] @ 7809
[ 90.570143] <1>.(1)[7809:insmod]memory_debug2 sself=00000000d65661d1, s=0000000067d823c8, 000000008eb19172, namepo00000000b1b57255 zhang, ssize:7,000000001263649f, bbpointer:000000008acc5477
[ 90.572310] <1>.(1)[7809:insmod]memory_debug3 s,name,size,bb:ffffffc028e99280,ffffffc028e99288,ffffffc028e99290,ffffffc028e99294
[ 90.573794] <1>.(1)[7809:insmod]Memory allocated successfully:00000000b34a50e2 00000000058c7568
[ 90.575044] <1>.(1)[7809:insmod]Physical address:68e98c80 68e98ce4
[ 90.575875] <1>.(1)[7809:insmod]initcall test_hello_init+0x0/0x184 [mem] returned 0 after 5611 usecs
可以看到物理地址0x68e98ce4-0x68e98c80 =0x64,即kmalloc物理地址上是连续的;
可以看到虚拟地址0xffffffc028e99294-0xffffffc028e99290=0x4, 及kmalloc虚拟地址是连续的;
同时要注意:
在使用%p打印内核内存地址时,内核会尝试保护自己,不打印真实地址。相反,它会为该地址打印一个不同的散列唯一标识符。所以确实需要打印响应地址,你可以使用%px。
其他一些物理虚拟地址转换接口:
struct page * alloc_pages(gfp_mask, order) // 分配 1<<order 个连续的物理页
struct page * alloc_page(gfp_mask) // 分配一个物理页
返回page对应的逻辑地址的:
__get_free_pages(gfp_mask, order) // 和alloc_pages一样,只不过返回的是第一个页的内核逻辑地址
__get_free_page(gfp_mask) // 返回一个页的逻辑地址
page_address(pages); //page_address( )宏的功能是获得物理页的逻辑地址
page_to_phys
phys_to_page
//把内核态虚拟地址转成物理地址
#define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))
//把物理内存地址转成内核态虚拟地址
#define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))
//把内核虚拟地址转成其内存单元对应page
#define virt_to_page(addr) pfn_to_page(virt_to_pfn(addr))
//把内存单元对应page转成内核虚拟地址
#define page_to_virt(page) pfn_to_virt(page_to_pfn(page))
//把内核态虚拟地址转成物理地址
#define __pa(x) __virt_to_phys((unsigned long)(x))
//把物理地址转成内核态虚拟地址
#define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x)))