linux获取内存(找内存基址的方法)

为什么选择内存管理:
早期的程序都是直接在物理地址上运行的,也就是说这个程序需要的空间没有超过机器的物理内存,所以不会有问题。然而,在实际场景中,存在多个任务和进程,因此为每个进程预留物理地址是不可靠的。举个栗子:如果有侏罗纪世界3节目A,C,A需要10M,需要100元,C需要20M,总内存120元m,按照之前的分配方式,第一个10M给A,Kramp-Karrenbauer 110元给,系统还剩10M,C需要20M。很明显剩余内存不够c用了,怎么办?
1元。效率问题
你可能会想,C程序运行的时候,程序的数据会写到磁盘,然后程序运行的时候,数据会从磁盘写回来。且不说无法满足和C程序并行运行的要求,就连频繁io操作带来的耗时问题都无法接受。
冠军联赛。进程地址隔离问题
除了效率问题,为一个进程保留的空间如果需要被其他进程访问,也会崩溃。比如进程A的访问空间是前10M,但是如果程序A中有一段代码访问10元,Kramp-Karrenbauer,110元,,就有可能导致程序崩溃,所以进程的地址空间需要相互隔离。
侏罗纪世界3。重新定位问题
实际上,单个任务不可能在分配的内存中运行。当多个任务并行运行时,在动态申请内存释放时,有可能在其他进程中申请地址。此时,有必要迁移到新的地址。
内存管理无非是想解决以上三个问题。如何提高内存的使用效率?如何隔离一个进程的地址空间?如何解决程序运行时的重定位问题?
管理内存如何从虚拟地址映射到物理地址:
管理内存从虚拟地址映射到物理地址的过程,就是解决上述侏罗纪世界3问题的过程。内存管理使用分段机制和分页机制分别解决上述侏罗纪世界3问题。一般流程如下:
分割机制:
只要程序是分段的,并且整个段被翻译到任意位置,段内的地址相对于段基址是不变的。不管段基址是什么,只要给出段内的偏移地址,cpu就能访问正确的指令。所以在加载用户程序时,只要将整个段内容复制到一个新的位置,然后将段基址寄存器中的地址改为这个地址,程序就能准确运行,因为程序中使用的是段内的偏移地址,这个偏移地址上的内容仍然与新的段基址相同。
可以看出,分段机制解决了进程间隔离和重定位的问题。
分页机制:
可以参考:010 Kramp-Karrenbauer 120元*了解一级页表的概念。为了兼容32元位和64元位,linux通常采用四级页表、页全局目录、页上级目录、页中间目录和页表:
这里不详细解释linux如何通过四级页表将线性地址(虚拟地址)转换成物理地址。网上有很多,推荐https://www.cnblogs.com/linhaostudy/Ma伊利/10038100 . html # autoid Kramp-Karrenbauer密室逃脱:锦标赛冠军Kramp-karren Bauer密室逃脱:锦标赛冠军Kramp-Karrenbauer 0元。
综上所述,linux通过使用段页结合的寻址方式,有效地解决了上述三个问题。下面用一张图详细描述一下:
当进程切换时,根据task_struct找到mm_struct中的pgd字段,得到新进程页面的全局目录,然后填充到CR3寄存器中,从而完成页面切换。
看看mmu分页寻址过程:
上部代码:
# include # include # include # include # include # include模块_描述(\ ‘虚拟地址到物理地址\ ‘);静态int pid静态无符号长整型va;module_param(pid,int,0644);//从命令行传递参数(变量,类型,权限)module_param(va,ulong,0644);//va表示的是虚拟地址static int find _ PGD _ init(void){ unsigned long pa=0;//pa表示的物理地址struct task _ struct * PCB _ tmp=;PGD _ t * PGD _ tmp=;pud _ t * pud _ tmp=;PMD _ t * PMD _ tmp=;pte _ t * pte _ tmp=;printk(KERN _ INFO \ ‘ PAGE_OFFSET=0x % LX \ n \ ‘,PAGE _ OFFSET).//页表中有多少个项/*pud和偏振模色散等等在线性地址中占据多少位*/printk(KERN _ INFO \ ‘ PGDIR _ SHIFT=% d \ n \ ‘,PGDIR _ SHIFT);//注意:在32位系统中羟基前列腺素脱氢酶和PUD是相同的printk(KERN _ INFO \ ‘ PUD _ SHIFT=% d \ n \ ‘,PUD _ SHIFT);printk(KERN _ INFO \ ‘ PMD _ SHIFT=% d \ n \ ‘,PMD _ SHIFT);printk(KERN _ INFO \ ‘ PAGE _ SHIFT=% d \ n \ ‘,PAGE _ SHIFT);printk(KERN _ INFO \ ‘ PTRS _佩尔_PGD=%d\n\ ‘,PTRS _佩尔_ PGD);//每个羟基前列腺素脱氢酶里面有多少个ptrs printk(KERN _ INFO \ ‘ PTRS _佩尔_普德=%d\n\ ‘,PTRS _佩尔_普德);printk(KERN _ INFO \ ‘ PTRS _佩尔_PMD=%d\n\ ‘,PTRS _佩尔_ PMD);//PMD中有多少个项printk(KERN _ INFO \ ‘ PTRS _佩尔_PTE=%d\n\ ‘,PTRS _佩尔_ PTE);printk(KERN _ INFO \ ‘ PAGE _ MASK=0x % LX \ n \ ‘,PAGE _ MASK);//页的掩码结构PID * p=;p=find _ vpid(PID);//通过进程的pid号数字找到结构pid的结构体pcb_tmp=pid_task(p,PID type _ PID);//通过pid的结构体找到进程的任务结构printk(KERN_INFO\’pgd=0x%p\n\ ‘,PCB _ tmp-mm-PGD);//判断给出的地址价值分析是否合法(vavm_end) if(!find_vma(pcb_tmp-mm,va)){ printk(KERN _ INFO ‘ virt _ addr0x % LX不可用. \n\ ‘,va);返回0;} pgd_tmp=pgd_offset(pcb_tmp-mm,va);//返回线性地址va,在页全局目录中对应表项的线性地址printk(KERN _ INFO \ ‘ PGD _ tmp=0x % p \ n \ ‘,PGD _ tmp);//pgd_val获得pgd_tmp所指的页全局目录项//pgd_val是将pgd_tmp中的值打印出来printk(KERN _ INFO \ ‘ PGD _瓦尔(* PGD _ tmp)=0x % LX \ n \ ‘,PGD _瓦尔(* PGD _ tmp));if(pgd_none(*pgd_tmp)){ //判断羟基前列腺素脱氢酶有没有映射未在羟基前列腺素脱氢酶中映射printk(KERN_INFO\ .\ n \ ‘);返回0;} pud_tmp=pud_offset(pgd_tmp,va);//返回价值分析对应的页上级目录项的线性地址printk(KERN _ INFO \ ‘ pud _ tmp=0x % p \ n \ ‘,pud _ tmp);printk(KERN _ INFO \ ‘ pud _ val(* pud _ tmp)=0x % LX \ n \ ‘,pud _ val(* pud _ tmp));if(pud _ none(* pud _ tmp)){ printk(KERN _ INFO \ ‘未在pud中映射. \ n \ ‘);返回0;} pmd_tmp=pmd_offset(pud_tmp,va);//返回价值分析在页中间目录中对应表项的线性地址printk(KERN _ INFO \ ‘ PMD _ tmp=0x % p \ n \ ‘,PMD _ tmp);printk(KERN _ INFO \ ‘ PMD _瓦尔(* PMD _ tmp)=0x % LX \ n \ ‘,PMD _瓦尔(* PMD _ tmp));if(PMD _ none(* PMD _ tmp)){ printk(KERN _ INFO \ ‘未在偏振模色散中映射. \ n \ ‘);返回0;} //在这里,把原来的pte_offset_map改成了pte _ offset _ kernel pte _ tmp=pte _ offset _ kernel(PMD _ tmp,va);//pte指的是找到表printk(KERN _ INFO \ ‘ pte _ tmp=0x % p \ n \ ‘,pte _ tmp);printk(KERN _ INFO \ ‘ pte _ val(* pte _ tmp)=0x % LX \ n \ ‘,pte _ val(* pte _ tmp));if(pte_none(*pte_tmp)){ //判断有没有映射printk(KERN_INFO\ ‘未在手提式测试装置中映射. \ n \ ‘);返回0;}如果(!pte _ present(* pte _ tmp)){ printk(KERN _ INFO \ ‘ pte不在随机存取存储中. \ n \ ‘);返回0;} pa=(pte _ val(* pte _ tmp)PAGE _ MASK);//物理地址的计算方法printk(KERN _ INFO ‘ virt _ addr0x % LX在随机存取存储页中是0x % LX .\n\ ‘,va,pa);//printk(0x % LX中的KERN _ INFO \ ‘ contect是0x%lx\n\ ‘,pa,*(无符号long *)((char *)pa PAGE _ OFFSET));返回0;}静态void _ _ exit find _ PGD _ exit(void){ printk(KERN _ INFO \ ‘再见!\ n \ ‘);}模块_初始化(find _ PGD _ init);模块_出口(找_ PGD _出口);模块许可证(\ ‘ GPL \ ‘);可以看出虚拟地址ffff99b488d48000对应的物理地址是80000000c8d48000。这个过程也是存储器管理单元的过程。
添加极客助手微信,加入技术交流群

其他教程

ps做通道教程(ps通道步骤)

2022-8-16 16:24:59

其他教程

怪咖遇奇葩走路萌哒哒801(怪咖遇奇葩走路蹦擦擦i)

2022-8-16 16:27:04

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索