linux简化了分段,在把虚拟空间的4G分成了内核和用户两个部分,每个用户都可以通过系统进入内核。这样从用户的角度看,他拥有整个虚拟内存。也就是用户自身的3G+内核的1G.内核的1G是所有用户共有的,而用户3G则是自己的私有财产,这也保证用户在处理问题的时候数据安全,使得用户与用户之间隔离。(从0xc0000000-0xfffffff的1G此段供内核使用)和用户(0x0000000-0xbfffffff的3G供进程使用).在linux看来虚拟地址与线性地址一致。因为地址映射变成了简单的线性地址映射,而计算机中是从低地址开始,则0xbffffff之后才是放在最高的内核的1G空间。所以offset就是0xc0000000(PAGE_OFFSET).
在系统启动时,会留出1mb的空间。在pc体系中,页框0由bios使用存放加电自检期间探测到的系统硬件配置。(在后来的linux中,有用程序实现pci设备的探测。)从物理地址0x000a0000-0x000fffff的范围是留给bios的例程,并且映射isa图形卡上的内部内存,此的物理地址被保留,但是对应的页框则不让os使用。早期时,内核询问bios以便了解物理内存大小,现在也调用bios过程建立一组物理地址范围和对应的内存类型。然后内核建立物理地址映射表,如果表可获取,则是在bios列表基础上构建内核。当不可获取时,内核按照缺省设置来构建此表:0x9f-0x100的所有页框都标记保留。
比如在128mb内存中:
从0x07ff0000-0x07ff2fff的物理地址范围中存放加电自检时bios写入的系统硬件设备信息;在初始化阶段,内核将这些信息拷贝到一个内核数据结构中,然后认为这些页框是可用的。
从0x07ff3000-0x07ffffff的物理地址范围被映射到硬件设备的rom芯片。
从0xffff0000开始的物理地址范围标记为保留,因为它由硬件映射到bios的rom芯片。
内核有可能看不到bios报告的所有物理内存。linux内核安装在ram的0x00100000开始的地方,从第二个mb开始。而所需要的页框则依赖于内核的配置方案。
通过上面的例子,就可以看出,内核映像在内核空间的起始地址就是0x00100000+PAGE_OFFSET=0xc0100000
而在后面的页初始化中看到这个:
load_cr3(swapper_pg_dir);将swapper_pg_dir写入cr3寄存器(在进程切换时也会如此,只不过那时会是下一个的pgd)。
cr3的作用就指向新进程的页目录pgd.类似的寄存器还有几个,各自的功能各不相同。而此时目录的起始地址在内核空间中是虚拟地址,此时就要转换。
跟踪会发现是这样定义的:
#define load_cr3(pgdir) asm volatile("movl %0,%%cr3": :"r" (__pa(pgdir)))将临时全局目录写入cr3.
#define __pa(x) ((unsigned long) (x) - PAGE_OFFSET)此的功能就是从开始的线性地址转换到物理地址。此时的虚拟地址减去偏移量就是物理地址。
除了cr3寄存器,还有就是cr0寄存器:
0位是保护允许位PE(Protedted Enable),用于启动保护模式,如果PE位置1,则保护模式启动,如果PE=0,则在实模式下运行。
1位是监控协处理位MP(Moniter coprocessor),它与第3位一起决定:当TS=1时操作码WAIT是否产生一个“协处理器不能使用”的出错信号。
2位是模拟协处理器位EM(Emulate coprocessor),如果EM=1,则不能使用协处理器,如果EM=0,则允许使用协处理器。
3位是任务转换位(Task Switch),当一个任务转换完成之后,自动将它置1。随着TS=1,就不能使用协处理器。
4位是微处理器的扩展类型位 ET(Processor Extension Type),其内保存着处理器扩展类型的信息,如果ET=0,则标识系统使用的是287协处理器,如果 ET=1,则表示系统使用的是387浮点协处理器。
31位是分页允许位(Paging Enable),它表示芯片上的分页部件是否允许工作。
关于分页机制,首先要开启它对应的pg。也就是31位,允许分页工作。
还有就是要从实模式转换到保护模式。
下面读关于它的汇编代码(arch/i386/kernel/head.S)
movl $(pg0 - __PAGE_OFFSET), %edi
movl $(swapper_pg_dir - __PAGE_OFFSET), %edx
movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */
此时因为是实模式还不是保护模式(我们还没有开启cr0的相关状态位)。所以指令寄存器中还是物理地址。而pg0是虚拟地址,所以要得出物理地址就是虚拟地址减去偏移量。
再看后面的0x007分解成为二进制:0x000000000111.这个二进制的后三位表明了u/s,r/w,p.分别以此为:含有访问页或页表所需的特权级别、含有页或页表的存取权限、所指的页或者页表在内存中。
如果放到初始化中来看,此时的页就是第一个。以此类推,8mb需要2k,而正好连续有4个页面。8mb就是内核对于物理内存的最低要求,就如如今两个人想结婚,最低要求最起码是异性。
接下来启动分页机制,看程序:
/* Enable paging */
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
这段程序中swapper_pg_dir,保存的是临时页全局目录。此时eax中保存的是物理地址。然后赋予cr3.
接下来将0x010000000000000000000000000000此时第31位为1,也就是开启分页机制,将cr0的31位设置为1.
CR1是未定义的控制寄存器,供将来的处理器使用。
CR2是页故障线性地址寄存器,保存最后一次出现页故障的全32位线性地址。
在后来的内核中,变成了这样:
movl $pa(swapper_pg_dir),%eax
movl %eax,%cr3
movl %cr0,%eax
orl $X86_CR0_PG,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
在head.S中:
ENTRY(swapper_pg_dir)
.fill 1024,4,0
34 extern pgd_t swapper_pg_dir[1024];
页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每个页表指向一个页框。组成线性地址的几个部分。
pgd(Page Global Directory)页全局变量
pud(page upper directory)页上级目录
pmd(page middle directory)页中间目录
pt(page table)页表
extern unsigned long long __supported_pte_mask;
typedef struct { unsigned long pte_low, pte_high; } pte_t;
typedef struct { unsigned long long pmd; } pmd_t;
typedef struct { unsigned long long pgd; } pgd_t;
typedef struct { unsigned long long pgprot; } pgprot_t;
linux 2.6源代码情景分析笔记之内存1
最新推荐文章于 2024-09-06 18:06:22 发布