如何从Linux内核中的任何地址获取结构页面

Gil*_*il' 25 virtual-memory linux-kernel

我有现有的代码,它采用一个列表struct page *并构建一个描述符表来与设备共享内存.该代码的上层当前需要一个与vmalloc用户空间分配的缓冲区,并用于vmalloc_to_page获取相应的缓冲区struct page *.

现在上层需要处理各种内存,而不仅仅是通过内存获得vmalloc.这可能是一个缓冲区kmalloc,内核线程堆栈中的指针,或其他我不知道的情况.我唯一的保证是这个上层的调用者必须确保所讨论的内存缓冲区在该点被映射到内核空间(即此时buffer[i]所有人都可以访问它0<=i<size).如何获得struct page*对应于任意指针的?

把它放在伪代码中,我有这个:

lower_layer(struct page*);
upper_layer(void *buffer, size_t size) {
    for (addr = buffer & PAGE_MASK; addr <= buffer + size; addr += PAGE_SIZE) {
        struct page *pg = vmalloc_to_page(addr);
        lower_layer(pg);
    }
}
Run Code Online (Sandbox Code Playgroud)

我现在需要改变upper_layer以应对任何有效的缓冲区(不改变lower_layer).

我发现virt_to_page,哪个Linux设备驱动程序指示"逻辑地址,[不]内存vmalloc或高内存".此外,is_vmalloc_addr测试地址是否来自vmalloc,并virt_addr_valid测试地址是否是有效的虚拟地址(饲料virt_to_page包括kmalloc(GFP_KERNEL)和内核堆栈).那么其他情况呢:全局缓冲区,高内存(有一天它会来,虽然我现在可以忽略它),可能还有其他我不知道的类型?所以我可以将我的问题重新表述为:

  1. 内核中所有类型的内存区域是什么?
  2. 我如何区分它们?
  3. 如何获取每个页面映射信息?

如果重要,代码在ARM(带有MMU)上运行,内核版本至少为2.6.26.

gby*_*gby 13

我想你想要的是一个页面表行走,类似于(警告,不是实际代码,锁定丢失等):

struct mm_struct *mm = current->mm;
pgd = pgd_offset(mm, address);
pmd = pmd_offset(pgd, address);  
pte = *pte_offset_map(pmd, address);  
page = pte_page(pte);
Run Code Online (Sandbox Code Playgroud)

但你应该非常小心这一点.你得到的kmalloc地址很可能不是页面对齐的例子.这听起来像是一个非常危险的API.

  • @Gilles:对于少于4个级别,内核会"折叠"它们,因此未使用的级别似乎只有一个条目,编译器会优化额外的调用. (2认同)

Lar*_*ars 7

将地址映射到结构页面

Linux要求具有将虚拟地址映射到物理地址以及将结构页面映射到其物理地址的快速方法.Linux通过知道全局mem_map数组在虚拟和物理内存中的位置来实现这一点,因为全局数组具有指向表示系统中物理内存的所有结构页的指针.所有架构都采用非常相似的机制实现这一点,但为了便于说明,我们只仔细检查x86.

将物理地址映射到虚拟内核地址

通过简单地减去PAGE_OFFSET,可以将任何虚拟地址转换为物理地址,这实际上是函数virt_to_phys()与宏__pa()的作用:

/* from <asm-i386/page.h> */
132 #define __pa(x)        ((unsigned long)(x)-PAGE_OFFSET)

/* from <asm-i386/io.h> */
 76 static inline unsigned long virt_to_phys(volatile void * address)
 77 {
 78         return __pa(address);
 79 }
Run Code Online (Sandbox Code Playgroud)

显然,反向操作涉及简单地添加PAGE_OFFSET,这是由函数phys_to_virt()用宏__va()执行的.接下来我们看看这有助于将struct页面映射到物理地址.

有一个例外,其中virt_to_phys()不能用于将虚拟地址转换为物理地址. 具体来说,在PPC和ARM体系结构中,virt_to_phys()不能用于转换函数consistent_alloc()返回的地址.consistent_alloc()用于PPC和ARM体系结构,以从非缓存中返回内存以供DMA使用.

内核中所有类型的内存区域是什么?<---见这里


Ces*_*arB 1

你可以试试virt_to_page。我不确定这是否是您想要的,但至少可以开始寻找。

  • 根据[LDD](http://lwn.net/images/pdf/LDD3/ch15.pdf),“virt_to_page”仅适用于逻辑地址,不适用于“vmalloc”缓冲区或高端内存。所以也许这是解决方案的一部分,但我还不够了解,无法确信我正在编写足够健壮的代码。 (3认同)