一,linux绕过了段机制而主要采用了页机制实现内存管理。原因如下:
1,为了使得内存管理变得简单,绕过了段机制,虚拟地址=线性地址。
2,为了方便移植到多个硬件平台,因为很多RISC处理器不支持段机制。
----------------------------------------------------------------------------------------------
二,进程的地址空间。
linux下每个进程都有4GB的地址空间=1GB的内核空间 + 3GB的用户空间
1,进程切换时,虚拟地址空间也随之切换。唯有3GB的用户空间切换,1GB的内核空间
是所用进程共享的,常驻内存。
2,虚拟地址空间切换,页表也随之切换。
----------------------------------------------------------------------------------------------
三,虚拟地址映射到物理地址。
地址映射应该分两部分分析,内核空间和用户空间。
内核空间使用的是线性映射,很好理解。
用户空间使用的是页表来映射。
---------------------------------------------------------------------------------------------
3.1,内核空间的地址映射到物理地址空间。
内核空间地址映射函数如下:
- #define __va(x) ((void *)((unsigned long)(x) PAGE_OFFSET)) // 虚拟地址 = 物理地址x + 3GB
- #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) //物理地址 = 虚拟地址x - 3GB
----------------------------------------------------------------------------------------------
3.2,用户空间映射到物理地址空间。
linux 采用了三级页表,实际上linux可以通过启用或禁用中间目录来启用两级和
三级分页(使用相同的代码),在32位的x86体系上就用的两级页表。
----------------------------------------------------------------------------------------------
先以两级页表说说虚拟地址是如何映射成为物理地址的吧。
进程切换,首先是从新的进程的PCB中读出pgd,然后写入CR3中,cr3 = __pa(pgd)
CR3相当于页表的根所在,CR3中存放的是页目录的基址,该基址 偏移量(线性地址的前10位)
= 页表的基址,页表的基址 偏移量(线性地址的中间10位)=物理页的基址,物理页的基址
偏移量 = 物理地址。
下面小结物理地址的计算:
-
cr3 Page Directory (10 MSB) = 指向 table_base
-
table_base Page Table (10 中间位) = 指向 page_base
-
page_base Offset = 物理地址 (获得页框)
----------------------------------------------------------------------------------------------
四,使用内核模块程序读出当前进程的CR3和pgd进行对比。
- #include <asm/system.h>
- #include <linux/mm.h>
- #include <linux/mm_types.h>
- #include <linux/sched.h>
- #include <linux/types.h>
- static unsigned long read_pgd(void)
- {
- return (unsigned long)current->mm->pgd;
- }
- static int __init printvm_init(void)
- {
- unsigned long cr3;
- unsigned long pgd = 0;
- cr3 = read_cr3();
- printk("cr3----------------->0x%lx\n",cr3);
- pgd = __pa(read_pgd());
- printk("pgd----------------->0x%lx\n",pgd);
- return 0;
- }
- static void __exit printvm_exit(void)
- {
- printk("--------------------->exit!\n");
- return ;
- }
- module_init(printvm_init);
- module_exit(printvm_exit);
内核里面写CR3:
结论:
pgd里面存放的是虚拟地址,通过__pa()后映射为物理地址,然后将该值写入到CR3.
----------------------------------------------------------------------------------------------
阅读(579) | 评论(0) | 转发(0) |