一种通过PID遍历进程所有物理页面的方法

一种通过PID遍历进程所有物理页面的方法

实现原理

    通过进程的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; //!! must be 0, or will show nothing T.T
}

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;
 
    // 获取PID对象
    proc_pid = find_get_pid(pid);
    if (!proc_pid)
        return NULL;
 
    // 通过PID对象获取进程描述符
    task = pid_task(proc_pid, PIDTYPE_PID);
    if (!task) {
        put_pid(proc_pid);
        return NULL;
    }
 
    // 如果不需要再使用proc_pid,则释放它
    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 = pgd_offset(mm, vaddr);
    if (pgd_none(*pgd) || pgd_bad(*pgd)) {
        // PGD条目不存在或无效
        return 0;
    }

    // 获取PUD(页上级目录)
    pud = pud_offset(pgd, vaddr);
    if (pud_none(*pud) || pud_bad(*pud)) {
        // PUD条目不存在或无效
        return 0;
    }

    // 获取PMD(页中间目录)
    pmd = pmd_offset(pud, vaddr);
    if (pmd_none(*pmd) || !pmd_present(*pmd)) {
        // PMD条目不存在或无效,或者页面不在内存中
        return 0;
    }

    // 使用pte_offset获取PTE(页表条目)
    pte = pte_offset_kernel(pmd, vaddr);
    if (!pte_present(*pte)) {
        // PTE条目不存在或无效
        return 0;
    }
	
	if (!(page = pte_page(*pte))){
        return 0;
	}
	phys = page_to_phys(page);
	
    // 获取页面帧号(PFN)
    pfn = pte_pfn(*pte);
    
	if(PageAnon(page))
	{
		//bit0为1,匿名映射
		printk("anon: pfn %lu, phy 0x%08lx, vaddr 0x%08lx\n", pfn, phys, vaddr);
	}
	else
	{
		//bit0为0,文件缓存
		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");
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值