1. 前言

Linux内核源码分析之setup_arch (二) 中介绍了当前启动阶段的内存分配函数memblock_alloc,该内存分配函数在本篇将要介绍paging_init中用于页表和内存的分配,paging_init函数大致流程如下图所示。

2. paging_init

2.1 build_mem_type_table

该函数根据具体的CPU架构对静态定义的mem_types数组中定义的属性进行调整。

2.2 prepare_page_table

该函数的作用是把页目录项清零,源码大致如下。首先是把虚拟地址范围[0, MODULES_VADDR]的页目录项清零,如果内核是模块区域以XIP方式运行的,则跳过内核部分的页目录,然后继续对区域[addr,PAGE_OFFSET]的页目录项清零,此时用户空间的页目录项已经全部清零;最后,把除了第一块内存条之外的内核空间[__phys_to_virt(end), VMALLOC_START]对应的页目录项清零。

/* arch/arm/mm/mmu.c */
static inline void prepare_page_table(void)
{
unsigned long addr;
phys_addr_t end;
/* <--(1)--> */
for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr)); #ifdef CONFIG_XIP_KERNEL
addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
#endif
/* <--(2)--> */
for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr)); end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
if (end >= lowmem_limit)
end = lowmem_limit;
/* <--(3)--> */
for (addr = __phys_to_virt(end);
addr < VMALLOC_START; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
}

2.3 map_lowmem

该函数将物理内存地址小于lowmem_limit的内存映射到内核空间,实际的内存映射工作在create_mapping中完成。

/* arch/arm/mm/mmu.c */
static void __init map_lowmem(void)
{
...
for_each_memblock(memory, reg) {
start = reg->base;
end = start + reg->size; if (end > lowmem_limit)
end = lowmem_limit;
if (start >= end)
break; map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
map.length = end - start;
map.type = MT_MEMORY; create_mapping(&map, false);
}
}

create_mapping函数的大致流程如下图所示,这里需要提一下,linux内核使用的是四级页表,即PGD、PUD、PMD、PTE;而ARM32使用的是二级页表,即PMD、PTE。同时由于内存管理是以页为单位进行的,如果按照ARM硬件MMU的分页机制,一个PMD对应的PTE并不能完全占用完整个页,为了避免内存浪费,会在软件层面上将两个PMD对应的PTE放在一个页内,具体细节可以参考文件arch/arm/include/asm/pgtable-2level.h中的注释部分。最终会调用alloc_init_pte函数对指定范围的内存区域进行映射,其中的early_pte_alloc函数最终也会去调用 Linux内核源码分析之setup_arch (二) 中介绍的memblock_alloc函数来分配内存,最后将PTE所在页写入到PMD中即可完成映射。

/* arch/arm/mm/mmu.c */
static void __init alloc_init_pte(...)
{
pte_t *start_pte = early_pte_alloc(pmd);
pte_t *pte = start_pte + pte_index(addr); do {
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
early_pte_install(pmd, start_pte, type->prot_l1);
}

2.4 devicemaps_init

该函数大致流程如下图所示,首先调用early_alloc分配一个页,然后调用early_trap_init将向量表复制到新的页内,最后调用create_mapping将这个页映射到0xffff0000处,如果mdesc->map_io存在,还会对设备相关的IO进行映射。

2.5 kmap_init

这个函数非常简单,把大小为2MB的区间[PKMAP_BASE,PAGE_OFFSET]映射到内核空间。

/* arch/arm/mm/mmu.c */
static void __init kmap_init(void)
{
#ifdef CONFIG_HIGHMEM
pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE),
PKMAP_BASE, _PAGE_KERNEL_TABLE);
#endif
}

3. 总结

本文主要介绍了内核启动阶段页表初始化部分的内容,其中,build_mem_type_table负责根据不同CPU架构对mem_types进行调整,prepare_page_table负责将待初始化区域的页目录项清零,然后通过map_lowmem建立低端内存区域的页表映射,最后调用devicemaps_init建立对向量表和设备IO的映射。至此,除了bootmem_init函数没有分析之外,paging_init基本算是分析完了,bootmem_init的分析将在下一篇中给出。

Linux内核源码分析之setup_arch (三)的更多相关文章

  1. Linux内核源码分析之setup_arch (四)

    前言 Linux内核源码分析之setup_arch (三) 基本上把setup_arch主要的函数都分析了,由于距离上一篇时间比较久了,所以这里重新贴一下大致的流程图,本文主要分析的是bootmem_ ...

  2. Linux内核源码分析之setup_arch (二)

    1. 概述 接着上一篇<Linux内核源码分析之setup_arch (一)>继续分析,本文首先分析arm_memblock_init函数,然后分析内核启动阶段的是如何进行内存管理的. 2 ...

  3. Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...

  4. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  5. Linux内核源码分析 day01——内存寻址

    前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...

  6. Linux内核源码分析方法_转

    Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...

  7. Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

    原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...

  8. Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  9. linux中断源码分析 - 中断发生(三)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 回顾 上篇文章linux中断源码分析 - 初始化(二)已经描述了中断描述符表和中断描述符数组的初始化,由于在初始 ...

随机推荐

  1. gitee模拟备份

    这个作业的要求在哪里 作业要求 团队名称 欢天喜地七仙女 团队成员 王玮晗.林鑫宇. 黄龙骏.陈少龙.何一山.崔亚明.陆桂莺 这个作业的目标 按照要求进行编码,按要求新建码云仓库.上传文件.拍摄视频 ...

  2. Alpha冲刺——总结

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺 作业正文 正文 其他参考文献 无 ...

  3. dubbo源码学习(一)dubbo容器启动流程简略分析

    最近在学习dubbo,dubbo的使用感觉非常的简单,方便,基于Spring的容器加载配置文件就能直接搭建起dubbo,之前学习中没有养成记笔记的习惯,时间一久就容易忘记,后期的复习又需要话费较长的时 ...

  4. python核心高级学习总结3-------python实现进程的三种方式及其区别

    python实现进程的三种方式及其区别 在python中有三种方式用于实现进程 多进程中, 每个进程中所有数据( 包括全局变量) 都各有拥有⼀份, 互不影响 1.fork()方法 ret = os.f ...

  5. 第7.8节 Python中隐秘的类封装方法

    前面章节已经介绍了Python中的多态和继承,本节将介绍面向对象程序设计OOP三大特征的另一个特征--封装. 一.    概念 封装是将对象的状态信息(也就是数据.属性)隐藏在对象内部,将对象的属性和 ...

  6. PyQt(Python+Qt)学习随笔: QDoubleSpinBox浮点数字设定部件简介

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在<PyQt(Python+Qt)学习随笔: ...

  7. 第8.33节 Python中__getattr__以及__getattr__与__ getattribute__的关系深入剖析

    一. 引言 前面几节分别介绍了Python中属性操作捕获的三剑客:__ getattribute__方法.__setattr__方法.__delattr__方法,为什么__ getattribute_ ...

  8. PyQt学习随笔:PyQt中捕获键盘事件后获取具体按键值的方法

    在PyQt中,如果要捕获键盘事件的具体按键,可以通过重写组件对象的keyPressEvent方法或event方法来捕获具体的按键,推荐使用keyPressEvent方法,因为event方法是一个通用事 ...

  9. webpack项目如何正确打包引入的自定义字体

    webpack项目如何正确打包引入的自定义字体 一. 如何在Vue或React项目中使用自定义字体 在开发前端项目时,经常会遇到UI同事希望在项目中使用一个炫酷字体的需求.那么怎么在项目中使用自定义字 ...

  10. Day1 【Scrum 冲刺博客】

    (因发作业当天没注意看作业内容,第一天的冲刺博客和第二天的同时发!!!不好意思!!!) 各个成员在 Alpha 阶段认领的任务 方晓莹 搭建社区管理系统的前端框架 登录页开发 管理员模块个人中心开发 ...