linux内核情景分析笔记-存储管理

linux内核情景分析笔记-存储管理

第2章 存储管理
LINUX页式管理
PGD          PMD          PT        PTE
页表目标     中间目录     页表     页表项

LINUX在32位地址下采取二层映射
#define PGDIR_SHIFT 22
#define PTRS_PER_PGD 1024

#define PMD_SHIFT 22
#define PTRS_PER_PMD 1

#define PTRS_PER_PTE 1024
根据以上宏定义,PMD被完美的架空了,而相当于采取了二层映射

其中PGD用了线性地址的最高10位 与  MMU 对应
线性地址的中间10位是所对应的PTE在PT中的索引
剩下的最低12位则是页中的偏移量

虚拟地址 = 段基地址:段偏移量
                            16位      32位
更准确的讲是段选择子了吧

在LINUX中段基地址 = 0(下面的____KERNEL_CS等),所以可以认为线性地址与虚拟地址总是相等的,但其本质不是一个东西


0xC0000000-0xFFFFFFFF为内核占用
0x0-0xBFFFFFFF为用户控件


内核的虚拟内存为简单的线性映射
#__PAGE_OFFSET (0xC0000000)
#define PAGE_OFFSET  ((unsigned long) __PAGE_OFFSET)
#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x) +PAGE_OFFSET)

__pa是从虚拟地址转换成物理地址
__va是从物理地址转换成虚拟地址


在GDT中有4个段描述符
其索引是2-5
分别是
__KERNEL_CS 内核代码段
__KERNEL_DS 内核数据段
__USER_CS 用户代码段
__USER_DS 用户数据段

#define start_thread(regs,new_eip,new_esp) do {\
 __asm__("movl %0,%%fs;movl %0,%%gs"::"r"(0)); \
 set_fs(USER_DS);
 regs->xds = __USER_DS; \
 regs->xes = __USER_DS; \
 regs->xss = __USER_DS; \
 regs->xcs = __USER_CS; \
 regs->eip = new_eip;   \
 regs->esp = new_esp;   \

}while(0)

通过这段宏可以看出,LINUX没用段式存储,虽然它也走了这个流程

 

MMU的流程 MMU使用物理地址

页式映射
从REG CR3拿PGD的地址
找到页面目录,线性地址中的高10位为索引,找到页面目录项,从中拿高20位作为页面表的索引,页面表与4k字节边界对齐,CPU自动补充前12位为0得到页面表地址。

然后拿线性地址的中间10位,得到页面表中的索引,拿到页面表项,页面表项的高20位在低位补充12个0,再加上线性地址的低12位组成物理地址。


mm_struct 任务相关的虚拟内存
vm_area_struct 一段虚拟内存的抽象,也可以理解为段
mm_struct中拥有vm_area_struct的指针
在vm_area_struct多的时候使用avl树来存储
mem_map_t  物理页表
zone_struct 物理内存的区结构,zone_struct把物理内存分成了几个部分
ZONE_DMA 0 供DMA使用
ZONE_NORMAL 普通使用
ZONE_HIGHMEN 高段内存,内核映射不到

物理内存之间区的划分并不是强制的,如果某一个区已经没有内存可用,是可以去别的区拿内存的

其实一直对内核的寻址有些疑问
不过刚刚似乎想通了
内核会做预映射,把PGD第768项以后的都做映射,也就是1G的空间
而这种映射应该是满足__pa()宏,即线性地址与物理地址是线性映射的。
所以最终__pa()宏被用作在内核代码中显性的获得某个线性地址所对应的物理地址
而MMU负责把一个线性地址隐式的转成了物理地址,而这已转换与内核代码无关。
不知这样理解是否正确?

今天只看到了这里
待续……

说起来把这么个东西放到首页很不好意思,主要目的是希望有看到的人帮我指正一下我所认知的错误或者解惑。谢谢啦:)

你可能感兴趣的:(linux内核情景分析笔记-存储管理)