实现原理
通过进程的mm_struct和虚拟地址查找对应的物理页面,虚拟地址可以通过vma缩小查找范围。
驱动代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/dcache.h>
#include <asm/fcntl.h>
#include <asm/processor.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/gpio.h>
#include <linux/sched/rt.h>
#include <uapi/linux/sched/types.h>
#include <linux/pid.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/compiler.h>
#include <linux/page-flags.h>
#define MY_DEV_NAME "my_dev"
#define TEST_PROC_DIR "page_test"
#define TEST_PROC_NAME "pid"
struct proc_dir_entry *test_proc_dir = NULL;
static int pid = -1;
static int test_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "echo pid to start.now:");
if(0 == pid)
{
seq_printf(m, "off\n");
}else
{
seq_printf(m, "%d\n", pid);
}
return 0;
}
static ssize_t test_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
char *tmp = kzalloc((count + 1), GFP_KERNEL);
if(!tmp){
return -ENOMEM;
}
memset(tmp, 0x00, count+1);
if(copy_from_user(tmp, buffer, count))
{
kfree(tmp);
return -EFAULT;
}
sscanf(tmp, "%d", &pid);
kfree(tmp);
return count;
}
static int test_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, test_proc_show, NULL);
}
static struct file_operations proc_fops = {
.owner = THIS_MODULE,
.open = test_proc_open,
.read = seq_read,
.write = test_proc_write,
.llseek = seq_lseek,
.release = single_release,
};
static int init_test_proc(void)
{
struct proc_dir_entry *file = NULL;
test_proc_dir = proc_mkdir(TEST_PROC_DIR, NULL);
if(NULL == test_proc_dir){
pr_err("%s Create %s failed\n", __func__, TEST_PROC_DIR);
return -EINVAL;
}
file = proc_create(TEST_PROC_NAME, 666, test_proc_dir, &proc_fops);
if(!file){
pr_err("%s Create %s failed\n", __func__, TEST_PROC_NAME);
return -EINVAL;
}
return 0;
}
static void proc_test_exit(void)
{
proc_remove(test_proc_dir);
}
static int test_open(struct inode *inode, struct file *file)
{
int major = MAJOR(inode->i_rdev);
int minor = MINOR(inode->i_rdev);
pr_info("%s: major=%d, minor=%d\n", __func__, major, minor);
return 0;
}
static int test_release(struct inode *inode, struct file *file)
{
pr_info("%s \n", __func__);
return 0;
}
static ssize_t test_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
pr_info("%s \n", __func__);
return 0;
}
static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
pr_info("%s \n", __func__);
return 0;
}
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_release,
.read = test_read,
.write = test_write
};
static struct miscdevice test_misc_device ={
.minor = MISC_DYNAMIC_MINOR,
.name = MY_DEV_NAME,
.fops = &test_fops,
};
struct task_struct *get_task_by_pid(pid_t pid) {
struct pid *proc_pid;
struct task_struct *task;
proc_pid = find_get_pid(pid);
if (!proc_pid)
return NULL;
task = pid_task(proc_pid, PIDTYPE_PID);
if (!task) {
put_pid(proc_pid);
return NULL;
}
put_pid(proc_pid);
return task;
}
unsigned long virt2pfn(struct mm_struct *mm, unsigned long vaddr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long pfn;
unsigned long phys;
struct page *page;
pgd = pgd_offset(mm, vaddr);
if (pgd_none(*pgd) || pgd_bad(*pgd)) {
return 0;
}
pud = pud_offset(pgd, vaddr);
if (pud_none(*pud) || pud_bad(*pud)) {
return 0;
}
pmd = pmd_offset(pud, vaddr);
if (pmd_none(*pmd) || !pmd_present(*pmd)) {
return 0;
}
pte = pte_offset_kernel(pmd, vaddr);
if (!pte_present(*pte)) {
return 0;
}
if (!(page = pte_page(*pte))){
return 0;
}
phys = page_to_phys(page);
pfn = pte_pfn(*pte);
if(PageAnon(page))
{
printk("anon: pfn %lu, phy 0x%08lx, vaddr 0x%08lx\n", pfn, phys, vaddr);
}
else
{
printk("file: pfn %lu, phy 0x%08lx, vaddr 0x%08lx\n", pfn, phys, vaddr);
}
return pfn;
}
static struct task_struct *thread_task;
static int pid_thread(void *arg)
{
struct task_struct *task;
struct mm_struct *mm;
struct vm_area_struct *vma = 0;
unsigned long vpage;
unsigned long pfn = 0;
while(pid == -1)
{
msleep(1000);
}
task = get_task_by_pid(pid);
if (!task) {
printk("Failed to find the process.\n");
return -1;
}
mm = task->mm;
if (!mm || !mm->pgd) {
printk("Invalid memory management information or page global directory is not initialized.\n");
return -1;
}
if (mm && mm->mmap){
for (vma = mm->mmap; vma; vma = vma->vm_next){
for (vpage = vma->vm_start; vpage < vma->vm_end; vpage += PAGE_SIZE){
pfn = virt2pfn(mm, vpage);
}
}
}
return 0;
}
static int __init test_init(void)
{
int ret;
pr_info("test_init\n");
ret = misc_register(&test_misc_device);
if (ret != 0 ) {
pr_err("failed to misc_register");
return ret;
}
thread_task = kthread_create(pid_thread, NULL, "pid-thread");
wake_up_process(thread_task);
init_test_proc();
pr_err("Minor number = %d\n", test_misc_device.minor);
return 0;
}
static void __exit test_exit(void)
{
pr_info("test_exit\n");
misc_deregister(&test_misc_device);
proc_test_exit();
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");