【问题标题】:How to get a struct page from any address in the Linux kernel如何从 Linux 内核中的任意地址获取结构页面
【发布时间】:2011-05-12 17:42:50
【问题描述】:

我现有的代码采用struct page * 的列表并构建描述符表以与设备共享内存。该代码的上层当前期望分配有vmalloc 或来自用户空间的缓冲区,并使用vmalloc_to_page 获取对应的struct page *

现在上层需要处理各种内存,而不仅仅是通过vmalloc获得的内存。这可能是使用kmalloc 获得的缓冲区、内核线程堆栈内的指针或我不知道的其他情况。我唯一的保证是这个上层的调用者必须确保有问题的内存缓冲区在那个时候被映射到内核空间中(即此时访问所有0<=i<sizebuffer[i] 是有效的)。 如何获取任意指针对应的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);
    }
}

我现在需要更改 upper_layer 以处理任何有效的缓冲区(不更改 lower_layer)。

我找到了virt_to_pageLinux Device Drivers 表示在“逻辑地址,[不是]来自vmalloc 的内存或高端内存”上运行。此外,is_vmalloc_addr 测试地址是否来自vmallocvirt_addr_valid 测试地址是否是有效的虚拟地址(virt_to_page 的素材;这包括kmalloc(GFP_KERNEL) 和内核堆栈)。其他情况呢:全局缓冲区、高内存(总有一天会出现,尽管我现在可以忽略它),可能还有其他我不知道的类型?所以我可以将我的问题重新表述为:

  1. 所有内核中的内存区域有哪些?
  2. 如何区分它们?
  3. 如何获取每个页面的映射信息?

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

【问题讨论】:

  • buffer 的目标是否需要任何对齐或大小要求?
  • @Karmastan:不,没有对齐约束。无论如何,较低层将映射整个页面。我可以用buffer &amp;= ~(PAGE_SIZE-1)(干净的等价物)开始upper_layer

标签: linux-kernel virtual-memory


【解决方案1】:

我猜你想要的是页表遍历,类似于(警告,不是实际代码,锁定丢失等):

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);

但是你应该非常小心。例如,您获得的 kmalloc 地址很可能不是页面对齐的。对我来说,这听起来像是一个非常危险的 API。

【讨论】:

  • 嗯,也许(现在去阅读和测试)。 address 有什么限制? (归根结底,缓冲区需要与设备共享,这意味着它跨越的所有页面都将被共享。代码现在正在为vmalloc 显然更难的情况工作。这不是外部的API,我问题中的上/下层只是我们的内部设计。)
  • 获得赏金是因为这有助于我更好地理解 vmalloc_to_page 正在做什么。但是在我的测试中,我只成功触发了一个 oops:“Unable to handle kernel paging request at virtual address 340009f4” when dereference ptep(由pte_offset_map返回),调用函数的堆栈上的地址或由kmalloc返回。
  • @Gilles:也许是因为内核正在为自己使用大页面(例如 2MB 页面)?在这种情况下,您执行页表遍历的方式会有所不同(并且可能不会给您一个结构页面 - 如果甚至存在的话)。 vmalloc_to_page 不关心可能是因为它知道它正在使用普通的 4K 页面。可以肯定的是,看看每个页表级别上的页表属性——如果它是一个大页面,那么其中一个级别必须有一个这样的属性(否则硬件本身无法知道)。跨度>
  • @Gilles:对于少于 4 个级别,内核会“折叠”它们,因此未使用的级别似乎只有一个条目,编译器会优化额外的调用。
  • @IgorR。不,我从来没有让那个代码在堆栈缓冲区中运行,现在整个驱动程序已经用不同的架构从头开始重写了。
【解决方案2】:

将地址映射到结构页面

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

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

任何虚拟地址都可以通过简单地减去 PAGE_OFFSET 转换为物理地址,这本质上就是带有宏 __pa() 的函数 virt_to_phys() 所做的:

/* 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 }

显然,反向操作只是简单地添加 PAGE_OFFSET,这是由函数 phys_to_virt() 和宏 __va() 执行的。接下来我们看看这如何帮助结构页面映射到物理地址。

有一个例外,virt_to_phys() 不能用于将虚拟地址转换为物理地址。 具体来说,在 PPC 和 ARM 架构上,virt_to_phys() 不能用于用于转换函数consistent_alloc()返回的地址。一致的分配()用于 PPC 和 ARM 架构,以从非缓存返回内存以用于 DMA。

What are all the kinds of memory zones in the kernel?

【讨论】:

  • 我已经读过了。我要问的是 LDD3 ch.15 或我对它的不完全理解缺少什么:什么是虚拟地址(实验上不是全局缓冲区的地址)?如何从虚拟(或任何其他)地址获取页面数据(结构页面,而不是物理地址)?
  • @Gilles:也许这篇文章有助于理解,什么是虚拟地址virtuell memory
【解决方案3】:

对于用户空间分配的内存,您想使用get_user_pages,它将为您提供与 malloc 内存关联的页面列表,并增加它们的引用计数器(您需要调用 page_cache_release完成后在每个页面上显示。)

对于 vmalloc 的页面,vmalloc_to_page 是你的朋友,我认为你不需要做任何事情。

【讨论】:

    【解决方案4】:

    对于64位架构,gby的答案应该适应:

     pgd_t * pgd;
     pmd_t * pmd;
     pte_t * pte;
     struct page *page = NULL;
     pud_t * pud;
     void * kernel_address;
    
     pgd = pgd_offset(mm, address);
     pud = pud_offset(pgd, address);
     pmd = pmd_offset(pud, address);
     pte = pte_offset_map(pmd, address);
     page = pte_page(*pte);
    
     // mapping in kernel memory:
     kernel_address = kmap(page);
    
     // work with kernel_address....
    
     kunmap(page);
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 根据LDDvirt_to_page 仅适用于逻辑地址,不适用于vmalloc 缓冲区或高内存。所以也许它是解决方案的一部分,但我没有足够的知识来确信我正在编写足够健壮的代码。
      猜你喜欢
      • 1970-01-01
      • 2011-09-09
      • 2018-12-10
      • 2012-04-17
      • 2017-04-26
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多