Linux 读写进程物理内存

研究Linux内存管理过程中,走了不少弯路,内存管理在Linux内核中是很重要,且很复杂的一部分,这里记录我对Linux内存管理结构的了解。

首先对涉及内存的几个术语解释:

虚拟地址空间:

        这是系统视图的地址空间,也是我们使用内存主要用到的地址空间

物理地址空间:

        CPU视图的地址空间,MMU会将虚拟地址转换为物理地址来访问内存。

MMU:

         内存管理单元,主要负责对内存管理(地址转换、访问控制等)

 

Linux内存映射原理:

     首先我们只能使用虚拟地址,但访问内存只能通过物理地址,所以虚拟地址首先会通过MMU转换为物理地址,然后经过总线就可以寻址到内存了,核心是 虚拟地址转 物理地址。

Linux使用4级页表来转换物理地址,每级页表存储虚拟内存地址的部分偏移,这里可以理解为索引吧,以下为4级页表解释

PGD 全局页目录

PUD 上层页面目录

PMD 中间页目录

PTE 页表项(页表项即为物理地址了)

下图说明了一个虚拟地址,被拆分了以下部分,每个部分对应于页表的偏移,当我们访问内存时,就是根据这些信息来获取内存的物理地址的。

 

 

 

当我们在程序内部使用变量内容时,如下:

int main(int argc,char *argv[])
{
    int *ptr=(int*)malloc(sizeof(unsigned int));
    *ptr=1992;
    while(1)
    {
        printf("pid %ld vaddr %ld value %d\n",getpid(),ptr,*ptr);
        sleep(2);
    }
    return 0;
}

这里指针变量存储了该内存对应的虚拟地址,当我们访问时该内存是,首先根据当前进程的实例(task_struct->mm),获取到进程的pgd,然后依次获取 pud、pmd、pte。

虚拟地址和物理地址之间的关联即通过页表的方式进行映射。

在内核中我们可以通过编写内核驱动的方式来访问任意进程的内存(内核中没有任何限制),内核读写任意进程内存核心实现代码如下:

static int vaddrtopaddr(struct task_struct *p,unsigned long vaddr)
{
	struct mm_struct *mm=p->mm;
	pgd_t *pgd;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
	unsigned long kks;
    unsigned long paddr = 0;
    unsigned long page_addr = 0;
    unsigned long page_offset = 0;
    
	pgd =pgd_offset(mm,vaddr);
    if (pgd_none(*pgd)) {
        printk("not mapped in pgd\n");
        return -1;
    }
 
    pud = pud_offset((p4d_t*)pgd, vaddr);
    printk("pud_val = 0x%lx\n", pud_val(*pud));
    if (pud_none(*pud)) {
        printk("not mapped in pud\n");
        return -1;
    }
 
    pmd = pmd_offset(pud, vaddr);
    if (pmd_none(*pmd)) {
        printk("not mapped in pmd\n");
        return -1;
    }
 
    pte = pte_offset_kernel(pmd, vaddr);
    if (pte_none(*pte)) {
        printk("not mapped in pte\n");
        return -1;
    }
	//页框地址 页的物理地址
	page_addr = pte_val(*pte) & PAGE_MASK;
    page_offset = vaddr & ~PAGE_MASK; //虚拟地址在物理地址中的偏移

	paddr = page_addr | page_offset; //内存的物理地址
	unsigned long _pageaddr=pte_page(*pte); //拿到物理地址对应的内存结构实例 page,这是Linux的物理内存管理结构,每个page结构实例表示一块物理内存

    //kmap要的是page结构的虚拟地址
    unsigned long vptr=kmap(_pageaddr); //将该物理地址映射到内核的虚拟地址空间中,这样才能访问该物理内存。
	if(vptr<=0)
	{
		printk("vptr is null");
	}
	else
	{
		printk("vptr 0x%lx vaddr value 0x%lx\n",vptr,vptr+page_offset);
		unsigned int *vint=(unsigned int*)(vptr+page_offset);
		printk("int val %d\n",*vint);
		*vint=2021;
	}
	kunmap(page_addr);
	return 1; 

}

完整代码 Git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值