简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!
优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀
优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀
优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门实战课【原创干货持续更新中……】🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 🌻2.Linux内核virt_to_page介绍
- 总结
- 🌻3.virt_to_page宏实现
- 🐓3.1 virt_to_page宏展开过程
- <1>.virt_to_page
- <2>.pfn_to_page展开
- <3>.__pfn_to_section展开
- 🌻4.virt_to_page代码实例
- 🐓4.1 字符设备驱动示例
- 🐓4.2 平台设备驱动示例
- 🐓4.3 SPI设备驱动示例
🌻1.前言
本篇目的:Linux内核之virt_to_page实现与用法实例
🌻2.Linux内核virt_to_page介绍
virt_to_page
是Linux内核中用于将虚拟地址转换为对应的页描述符(page descriptor)的函数。在Linux内核中,每个物理内存页都有一个对应的struct page
结构,该结构包含了页的详细信息,如页的状态、引用计数、映射信息等。当内核需要操作一个特定的虚拟地址时,它通常会使用virt_to_page
来获取该虚拟地址对应的page
结构。- 在Linux内核中,虚拟地址空间被划分为多个区域,如内核空间、用户空间等。每个区域内部又被划分为多个页,每页大小通常为4KB。内核使用页表来管理虚拟地址到物理地址的映射。
virt_to_page
函数利用这些信息来查找给定虚拟地址对应的page
结构。 - 当
virt_to_page
被调用时,它首先会检查提供的虚拟地址是否有效,即是否属于内核地址空间。如果地址有效,函数会进一步确定该地址所在的页表项,并从中提取出对应的物理页号(PFN)。PFN是物理页的一个唯一标识符。一旦PFN被确定,virt_to_page
会通过PFN来查找对应的page
结构。 page
结构是内核中表示物理页的主要数据结构,它包含了页的各种属性,如页的状态(是否被分配、是否脏等)、页的访问权限、页的映射信息等。通过page
结构,内核可以执行各种内存管理操作,如页的分配、释放、标记脏页以便回写等。virt_to_page
的使用在内核中非常普遍,尤其是在内存管理、页缓存、文件系统等模块中。例如,当内核需要访问一个特定的内核虚拟地址时,它可能会使用virt_to_page
来获取对应的page
结构,然后检查页的状态或执行其他操作。- 然而,值得注意的是,
virt_to_page
通常只应用于内核虚拟地址。对于用户空间虚拟地址,内核使用不同的函数来获取对应的page
结构,因为用户空间的虚拟地址可能涉及到更复杂的内存管理机制,如页表共享、写时复制等。 virt_to_page
是Linux内核中的一个关键函数,它简化了内核对虚拟地址到物理页的转换过程,使得内核能够高效地管理内存资源。
总结
-
virt_to_page
是 Linux 内核中的一个函数,它的作用是将一个虚拟地址转换为对应的struct page
结构体指针。在 Linux 内核中,struct page
结构体用于表示物理内存页。这个函数在内核中用于管理虚拟地址和物理地址之间的映射关系,通常在内核代码中进行内存管理和页表操作时会用到。 -
具体而言,当内核需要访问某个虚拟地址对应的物理页时,可以使用
virt_to_page
函数将虚拟地址转换为对应的struct page
结构体指针,然后通过这个指针可以获取到物理页的相关信息,比如页面状态、页面的引用计数等。 -
这个函数在内核的内存管理、页面交换、页面分配等方面都有着重要的作用,能够帮助内核更加高效地管理系统内存。
🌻3.virt_to_page宏实现
🐓3.1 virt_to_page宏展开过程
<1>.virt_to_page
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
<2>.pfn_to_page展开
#define pfn_to_page __pfn_to_page
#define __pfn_to_page(pfn) \
({ unsigned long __pfn = (pfn); \
struct mem_section *__sec = __pfn_to_section(__pfn); \
__section_mem_map_addr(__sec) + __pfn; \
})
<3>.__pfn_to_section展开
static inline struct mem_section *__pfn_to_section(unsigned long pfn)
{
return __nr_to_section(pfn_to_section_nr(pfn));
}
🌻4.virt_to_page代码实例
🐓4.1 字符设备驱动示例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/mm.h> // 包含 virt_to_page 函数
#define DEVICE_NAME "virt_to_page_example"
#define BUF_SIZE 1024
static char buffer[BUF_SIZE];
static unsigned long vaddr = 0x12345678; // 要处理的虚拟地址
static int device_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "Device opened.\n");
return 0;
}
static ssize_t device_read(struct file *file, char *buf, size_t count, loff_t *offset) {
struct page *page_ptr = virt_to_page(vaddr); // 将虚拟地址转换为页面指针
// 将页面内容复制到用户空间
if (copy_to_user(buf, page_address(page_ptr), count)) {
printk(KERN_ERR "Error copying data to user.\n");
return -EFAULT;
}
return count;
}
static struct file_operations fops = {
.open = device_open,
.read = device_read,
};
static int __init init_module(void) {
printk(KERN_INFO "Initializing virt_to_page example module.\n");
register_chrdev(0, DEVICE_NAME, &fops); // 注册字符设备驱动
return 0;
}
static void __exit cleanup_module(void) {
printk(KERN_INFO "Cleaning up virt_to_page example module.\n");
unregister_chrdev(0, DEVICE_NAME); // 注销字符设备驱动
}
MODULE_LICENSE("GPL");
module_init(init_module);
module_exit(cleanup_module);
🐓4.2 平台设备驱动示例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/mm.h> // 包含 virt_to_page 函数
#define DRIVER_NAME "virt_to_page_platform_driver"
#define VADDR 0x87654321 // 要处理的虚拟地址
static struct resource *res;
static int my_platform_driver_probe(struct platform_device *pdev) {
struct page *page_ptr = virt_to_page(VADDR); // 将虚拟地址转换为页面指针
// 在这里可以使用 page_ptr 对页面进行操作
return 0;
}
static int my_platform_driver_remove(struct platform_device *pdev) {
return 0;
}
static struct platform_driver my_platform_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = my_platform_driver_probe,
.remove = my_platform_driver_remove,
};
static int __init my_platform_driver_init(void) {
return platform_driver_register(&my_platform_driver); // 注册平台设备驱动
}
static void __exit my_platform_driver_exit(void) {
platform_driver_unregister(&my_platform_driver); // 注销平台设备驱动
}
MODULE_LICENSE("GPL");
module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
🐓4.3 SPI设备驱动示例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/mm.h> // 包含 virt_to_page 函数
#define DRIVER_NAME "virt_to_page_spi_driver"
#define VADDR 0xAABBCCDD // 要处理的虚拟地址
static int my_spi_probe(struct spi_device *spi) {
struct page *page_ptr = virt_to_page(VADDR); // 将虚拟地址转换为页面指针
// 在这里可以使用 page_ptr 对页面进行操作
return 0;
}
static int my_spi_remove(struct spi_device *spi) {
return 0;
}
static struct spi_driver my_spi_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = my_spi_probe,
.remove = my_spi_remove,
};
static int __init my_spi_init(void) {
return spi_register_driver(&my_spi_driver); // 注册SPI设备驱动
}
static void __exit my_spi_exit(void) {
spi_unregister_driver(&my_spi_driver); // 注销SPI设备驱动
}
MODULE_LICENSE("GPL");
module_init(my_spi_init);
module_exit(my_spi_exit);