简单的看法:线性空间跟物理空间在同一时间必须是一一映射的关系,之所以物理空间可以比较少, 是因为允许空映射的存在, 以及利用了时分复用, 这样不同的时候, 可以有不同的线性地址映射到同一物理地址但如果物理空间大呢? 也一样, 同一时间, 只能是一一映射, 这个时候线性空间就没办法利用全部的物理空间。但也可以利用时分复用, 即下一个时候, 线性空间可以指向之前无法用到的物理空间下面假设物理内存比较大对linux系统来说, 一个进程可以有4G的线性空间, 其中3G是用户空间的, 1G是内核空间。 3G 的用户空间每个进程都不同, 所以不能用来映射物理空间, 因为内核不知道到底该怎么映射。 1G内核空间是固定的, 可以用来映射物理空间, 当它最多映射1G的物理内存, 所以linux的做法是前896M用来映射物理内存的前896M, 这896M一般不变, 剩下的128M用来作时分复用, 即在不同时间映射到不同的物理内存, 这样就可以使用896M以上的内存了接着的是比较杂乱的知识:32位系统的虚拟内存空间是4G, 其中3G用于用户空间, 1G用于内核空间物理内存的大小不定,但内核如果要对其中的一段物理内存进行操作, 必须先将它映射到自身的1G虚拟内存中, 所有内存同一时间可以处理的物理内存少于1G(1G虚拟内存有一部分要用作别的用途), 当然不同时间可以操作不同的物理内存。对内核空间的映射, 采用简单的线性映射,即加一个偏移量,物理的0地址+PAGESET就映射到了虚拟内存中内核空间的起始位置linux的内存模型采用了节点(node)跟区域的概念(zone), 一个节点包含一下3个区域(如果一个物理内存只被当作一个节点,那么它就被划分为下面3个部分):ZONE_DMA(0-16m): 给设备IO传输数据用的, 不需要地址转换?ZONE_NORMAL(16m-896m): 这部分被映射到内核的1G虚拟内存的低部分, 是内核可以直接操作的ZONE_HIGHMEM(896m-):在x86上,内核1G虚拟空间中,有128m内存是用来保存page table等内核数据结构(最后128M),这部分不被映射到物理内存,虚拟空间就只剩下896m用来映射,所以ZONE_NORMAL只到896m。内核维护着一组自己使用的页表,驻留在所谓主内核页全局目录(master kernel page global directory)中。系统初始化后,这组页表永远不会被任何进程或者任何内核线程直接使用;更确切第说,主内核页全局目录的最高目录项部分作为参考模 型,为系统中每个普通进程对应的页全局目录项提供模板。 0x2ff |--------------------| | | | | | | |--------------------| | |Provisional kernel Page Tables |--------------------|_end | | | |Uninitialized kernel data | | |--------------------|_edata | | | | | |Initialized kernel data | | |--------------------|_etext | | | | | |Kernel code | | | | 0x100 | | |--------------------|_text (0x00100000) | | | |Unavailable page fames 0x9f | | |--------------------| | | | | | |Available page fames | | | | 1 | | |--------------------| 0 | |Unavailable page fames |--------------------| Page frame # Linux 2.6的前768个页框(3MB) 物理内存前3MB布局示意图内核在初始化的第一阶段,可以通过与物理地址相同的线性地址或者通过从0xc0000000开始的8MB线性地址对RAM的前8MB进行寻址:- 0项和0x300(768)项的地址字段置为pg0的物理地址,而1项和0x301(769)项的地址字段置为pg1的物理地址
- 把这四项的Present、Read/Write、User/Supervisor标志置位
- 把这四项的Accessed、Dirty、 PCD、 PWD、 Page Size标志清0
页全局目录放在swapper_pg_dir变量中,而映射前8MB RAM的两个目录项是pg0和pg1:extern pgd_t swapper_pg_dir[1024];typedef struct { unsigned long long pgd; } pgd_t; |--------------------| 1023 | | | |(对应128MB虚拟空间) | | |--------------------| | | | | | | | | | | | | |--------------------| 769 | |pg1 --> 5~8MB |--------------------| 768 | |pg0 --> 1~4MB |--------------------| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |--------------------| 1 | |pg1 --> 5~8MB |--------------------| 0 | |pg0 --> 1~4MB %%cr3------->|--------------------| Provisional kernel Page Tables(swapper_pg_dir)线性地址:0xc000 0000的高20位为1100000000(2)=768当RAM小于896MB时的最终内核页表:- 内核页表所提供的最终映射必须把从0xc000 0000开始的内核线性地址转化为从0开始的物理地址
- 宏_pa用于把从PAGE_OFFSET开始的线性地址转换成相应的物理地址,而宏_va做相反的转化
- 主内核页全局目录(The master kernel Page Global Directory)仍然保存在swapper_pg_dir变量中。它由paging_init()函数初始化
- 线性地址的最高128MB留给几种映射取用,因此剩余的映射RAM的内核地址空间为1GB - 128MB = 896MB
进程间前3G的线性空间是不同的, 而最后1G内核空间都是相同的, 即有着同样的页表目录项跟页表, 这些页表目录称为master kernel page global directory, 保存在swapper_pg_dir(数组1024项)中
内核的启动分两个阶段:
第一阶段建立了页表目录项中的2个项, 既有2个页表, 每个页表一般有1024个项, 这样就总共可以映射8M的内存空间。启动分页?
这个8M的就可以被使用了(这个8M的映射是通过线性映射来的, 内核来静态的初始化它)
第二阶段初始化所有1G线性空间的对应的256个页表全部初始化, 对物理内存进行映射, 从页表的第768项开始, 因为之前的已经映射好了
pagetable_init() --> kernel_physical_mapping_init()static void __init kernel_physical_mapping_init(pgd_t *pgd_base){ unsigned long pfn; pgd_t *pgd; pmd_t *pmd; pte_t *pte; int pgd_idx, pmd_idx, pte_ofs;定位主内核页全局目录(master kernel page global directory)的起始项pgd=768:|--------------------------------------|| pgd_idx = pgd_index(PAGE_OFFSET); || pgd = pgd_base + pgd_idx; ||--------------------------------------|物理地址从0x0000 0000开始,起始页框号(page frame number)为pfn;|-----------------|| pfn = 0; ||-----------------| for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {直接返回pgd(pmd和pgd指向同一个页目录项)|-------------------------------------|| pmd = one_md_table_init(pgd); ||-------------------------------------| if (pfn >= max_low_pfn) continue; for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {计算第pfn个页框对应的内核空间的线性地址:|-----------------------------------------------------------------|| unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET; ||-----------------------------------------------------------------| if (cpu_has_pse) { unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1; if (is_kernel_text(address) || is_kernel_text(address2)) set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC)); else set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE)); pfn += PTRS_PER_PTE;填写一个页目录项pmd(pgd),并填写该目录项所对应的页表的所有项为页目录项pmd分配页表pte,将该页表pte的物理地址写入pmd中,并初始化页表pte的每个页表项;
|--------------------------------------------------------------------------|| } else { || pte = one_page_table_init(pmd); || for (pte_ofs = 0; || pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; || pte++, pfn++, pte_ofs++) { | | if (is_kernel_text(address)) || set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); || else || set_pte(pte, pfn_pte(pfn, PAGE_KERNEL)); || } || } ||--------------------------------------------------------------------------| } }}
阅读(207) | 评论(0) | 转发(0) |