27

我有现有的代码,它获取列表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);
    }
}

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

我发现virt_to_pageLinux 设备驱动程序指示在“逻辑地址,[不是]来自内存vmalloc或高内存”上运行。此外,is_vmalloc_addr测试一个地址是否来自vmalloc,并virt_addr_valid测试一个地址是否是一个有效的虚拟地址(用于virt_to_page; 这包括kmalloc(GFP_KERNEL)内核堆栈)。其他情况呢:全局缓冲区、高内存(总有一天会出现,尽管我现在可以忽略它),可能还有其他我不知道的类型?所以我可以将我的问题重新表述为:

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

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

4

5 回答 5

15

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

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。

于 2011-05-23T15:42:48.837 回答
7

将地址映射到结构页面

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() 不能用于转换由函数一致_alloc() 返回的地址。一致的分配()用于 PPC 和 ARM 架构,以从非缓存返回内存以用于 DMA。

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

于 2011-05-23T16:21:24.117 回答
3

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

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

于 2014-08-27T17:25:32.913 回答
2

对于 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);
于 2015-06-04T13:03:06.287 回答
1

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

于 2011-05-16T09:51:18.763 回答