如何从Linux内核中的任意地址获取struct page
我现有的代码采用 struct page * 列表并构建描述符表以与设备共享内存。该代码的上层当前期望使用 vmalloc
或从用户空间分配缓冲区,并使用 vmalloc_to_page
获取对应的struct page *
。
现在上层需要应对各种内存,而不仅仅是通过vmalloc
获得的内存。这可能是使用 kmalloc 获得的缓冲区、内核线程堆栈内的指针或我不知道的其他情况。我唯一的保证是这个上层的调用者必须确保有问题的内存缓冲区在那时映射到内核空间中(即,访问所有 buffer[i]
是有效的。此时代码>0<=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< /code> 处理任何有效的缓冲区(不更改
lower_layer
)。
我找到了 virt_to_page
,其中 Linux 设备驱动程序 表示对“逻辑地址,[不是]来自
vmalloc
的内存或高端内存”进行操作。此外,is_vmalloc_addr
测试地址是否来自 vmalloc
,以及 virt_addr_valid
测试地址是否是有效的虚拟地址(virt_to_page
的素材;这包括 kmalloc(GFP_KERNEL)
和内核堆栈)。其他情况怎么样:全局缓冲区、高内存(总有一天会出现,尽管我现在可以忽略它),可能是我不知道的其他类型?所以我可以将我的问题重新表述为:
- 内核中的内存区域有哪些类型?
- 我如何区分它们?
- 如何获取它们各自的页面映射信息?
如果重要的话,代码在 ARM(带有 MMU)上运行,并且内核版本至少为 2.6.26。
I have existing code that takes a list of struct page *
and builds a descriptor table to share memory with a device. The upper layer of that code currently expects a buffer allocated with vmalloc
or from user space, and uses vmalloc_to_page
to obtain the corresponding struct page *
.
Now the upper layer needs to cope with all kinds of memory, not just memory obtained through vmalloc
. This could be a buffer obtained with kmalloc
, a pointer inside the stack of a kernel thread, or other cases that I'm not aware of. The only guarantee I have is that the caller of this upper layer must ensure that the memory buffer in question is mapped in kernel space at that point (i.e. it is valid to access buffer[i]
for all 0<=i<size
at this point). How do I obtain a struct page*
corresponding to an arbitrary pointer?
Putting it in pseudo-code, I have this:
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);
}
}
and I now need to change upper_layer
to cope with any valid buffer (without changing lower_layer
).
I've found virt_to_page
, which Linux Device Drivers indicates operates on “a logical address, [not] memory from vmalloc
or high memory”. Furthermore, is_vmalloc_addr
tests whether an address comes from vmalloc
, and virt_addr_valid
tests if an address is a valid virtual address (fodder for virt_to_page
; this includes kmalloc(GFP_KERNEL)
and kernel stacks). What about other cases: global buffers, high memory (it'll come one day, though I can ignore it for now), possibly other kinds that I'm not aware of? So I could reformulate my question as:
- What are all the kinds of memory zones in the kernel?
- How do I tell them apart?
- How do I obtain page mapping information for each of them?
If it matters, the code is running on ARM (with an MMU), and the kernel version is at least 2.6.26.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我想你想要的是页表遍历,比如(警告,不是实际代码,锁定丢失等):
但是你应该非常小心这一点。例如,您获得的 kmalloc 地址很可能不是页面对齐的。对我来说,这听起来是一个非常危险的 API。
I guess what you want is a page table walk, something like (warning, not actual code, locking missing etc):
But you you should be very very careful with this. the kmalloc address you got might very well be not page aligned for example. This sounds like a very dangerous API to me.
将地址映射到结构页
Linux 需要有一种快速方法将虚拟地址映射到物理地址以及将结构页映射到其物理地址。 Linux 通过了解全局 mem_map 数组在虚拟内存和物理内存中的位置来实现此目的,因为全局数组具有指向表示系统中物理内存的所有结构页的指针。所有架构都通过非常相似的机制来实现这一点,但是,出于说明目的,我们将仅仔细检查 x86。
将物理地址映射到虚拟内核地址
任何虚拟地址都可以通过简单地减去 PAGE_OFFSET 来转换为物理地址,这本质上就是带有宏 __pa() 的函数 virt_to_phys() 所做的事情:
显然,相反的操作只需添加 PAGE_OFFSET,即由函数 phys_to_virt() 和宏 __va() 执行。接下来我们看看这如何帮助将结构页映射到物理地址。
有一个例外,virt_to_phys() 不能用于将虚拟地址转换为物理地址。具体来说,在 PPC 和 ARM 架构上,virt_to_phys() 不能用于转换函数confirm_alloc()返回的地址。 concent_alloc() 用于 PPC 和 ARM 架构,从非缓存返回内存以供 DMA 使用。
内核中的内存区域有哪些种类? < ;---看这里
Mapping Addresses to a struct page
There is a requirement for Linux to have a fast method of mapping virtual addresses to physical addresses and for mapping struct pages to their physical address. Linux achieves this by knowing where, in both virtual and physical memory, the global mem_map array is because the global array has pointers to all struct pages representing physical memory in the system. All architectures achieve this with very similar mechanisms, but, for illustration purposes, we will only examine the x86 carefully.
Mapping Physical to Virtual Kernel Addresses
any virtual address can be translated to the physical address by simply subtracting PAGE_OFFSET, which is essentially what the function virt_to_phys() with the macro __pa() does:
Obviously, the reverse operation involves simply adding PAGE_OFFSET, which is carried out by the function phys_to_virt() with the macro __va(). Next we see how this helps the mapping of struct pages to physical addresses.
There is one exception where virt_to_phys() cannot be used to convert virtual addresses to physical ones. Specifically, on the PPC and ARM architectures, virt_to_phys() cannot be used to convert addresses that have been returned by the function consistent_alloc(). consistent_alloc() is used on PPC and ARM architectures to return memory from non-cached for use with DMA.
What are all the kinds of memory zones in the kernel? <---see here
对于用户空间分配的内存,您需要使用
get_user_pages
,它将为您提供与 malloc 内存关联的页面列表,并增加它们的引用计数器(您需要调用 < code>page_cache_release 在每个页面上完成后。)对于 vmalloc 的页面,
vmalloc_to_page
是你的朋友,我认为你不需要做任何事情。For user-space allocated memory, you want to use
get_user_pages
, which will give you the list of pages associated with the malloc'd memory, and also increment their reference counter (you'll need to callpage_cache_release
on each page once done with them.)For vmalloc'd pages,
vmalloc_to_page
is your friend, and I don't think you need to do anything.对于64位架构,gby的答案应该调整为:
For 64 bit architectures, the answer of gby should be adapted to:
您可以尝试
virt_to_page< /代码>
。我不确定这是否是您想要的,但至少它是一个可以开始寻找的地方。
You could try
virt_to_page
. I am not sure it is what you want, but at least it is somewhere to start looking.