我现有的代码采用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_page
,其中Linux Device Drivers表示对“vmalloc
或高内存的逻辑地址,[非]内存”进行操作。此外,is_vmalloc_addr
测试地址是来自vmalloc
还是virt_addr_valid
测试地址是否为有效的虚拟地址(virt_to_page
的饲料;这包括kmalloc(GFP_KERNEL)
和内核堆栈)。那么其他情况呢:全局缓冲区,高内存(有一天它会来,虽然我现在可以忽略它),可能还有其他我不知道的类型?所以我可以将我的问题重新表述为:
如果重要,代码在ARM上运行(带有MMU),内核版本至少为2.6.26。
答案 0 :(得分: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);
但是你应该非常小心这一点。你得到的kmalloc地址很可能不是页面对齐的例子。这听起来像是一个非常危险的API。
答案 1 :(得分: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 }
显然,反向操作只需添加PAGE_OFFSET,这是由函数phys_to_virt()用宏__va()执行的。接下来,我们将看到这有助于将struct页面映射到物理地址。
有一个例外,其中virt_to_phys()不能用于将虚拟地址转换为物理地址。具体来说,在PPC和 ARM体系结构上,virt_to_phys()不能用于转换函数consistent_alloc()返回的地址。在PPC和ARM体系结构上使用consistent_alloc()来从非缓存中返回内存,以便与DMA一起使用。
What are all the kinds of memory zones in the kernel?&lt; ---见这里
答案 2 :(得分:1)
您可以尝试virt_to_page
。我不确定这是你想要的,但至少它是开始寻找的地方。
答案 3 :(得分:1)
对于用户空间分配的内存,您希望使用get_user_pages
,它将为您提供与malloc内存关联的页面列表,并且还会增加其引用计数器(您可以使用#l}需要在完成每个页面后调用page_cache_release
。)
对于vmalloc页面,vmalloc_to_page
是您的朋友,我认为您不需要做任何事情。
答案 4 :(得分:1)
对于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);