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. TensorFlow安装方法:附带坑解决办法

    >>添加Anaconda 仓库的镜像 Anaconda 安装包可以到 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 下载. ...

  2. 利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果

    利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果 前言 近日公司接到一个轨道系统的需求,需要将地铁线路及列车实时位置展示在大屏上.既然是大屏项目,那视觉效果当然是第一重点,咱们可以先来看看项 ...

  3. PyQt(Python+Qt)学习随笔:Qt Designer中部件的调色板palette属性和字体font属性设置

    一.调色板 在Qt Designer的部件属性中,有个部件调色板(palette)的属性,进入后,如下图所示: 1.调色板palette Qt中提供的调色板palette用于管理控件的外观显示,对应P ...

  4. 爬虫模块-requests

    title: python爬虫01 date: 2020-03-08 22:56:12 tags: 1.requests模块 requests模块的底层是urllib,但是比urllib更强大也更加简 ...

  5. 《30天自制操作系统》软盘 -> VMware虚拟机

    书名叫做30天自制操作系统,按照学校课设答辩的时间来看,估计得把书名改成<一周自制操作系统>,太卷了哈哈哈 我们可以使用qemu来模拟物理机 make run第二天制作的操作系统 可以看到 ...

  6. Gif开发笔记(一):gif介绍、编译和工程模板

      前言   实现gif图片的解码和生成.   Gif 简介   GIF格式的名称是Graphics Interchange Format的缩写,是在1987年由Compu Serve公司为了填补跨平 ...

  7. Scrum 冲刺 第三篇

    Scrum 冲刺 第三篇 每日会议照片 昨天已完成工作 队员 昨日完成任务 黄梓浩 继续完成app项目架构搭建 黄清山 完成部分个人界面模块数据库的接口 邓富荣 完成部分后台首页模块数据库的接口 钟俊 ...

  8. MySQL存储引擎:MyISAM和InnoDB的区别

    MyISAM和InnoDB的区别 定义 InnoDB:MySQL默认的事务型引擎,也是最重要和使用最广泛的存储引擎.它被设计成为大量的短期事务,短期事务大部分情况下是正常提交的,很少被回滚.InnoD ...

  9. 密码学系列之:明文攻击和Bletchley Park

    目录 简介 crib和明文攻击 布莱奇利公园(Bletchley Park) 简介 明文攻击就是指已经知道了部分明文和它对应的加密后的字段,从而可以推测出使用的加密手段或者密码本.明文攻击这个故事还要 ...

  10. Angular:组件之间的通信@Input、@Output和ViewChild

    ①父组件给子组件传值 1.父组件: ts: export class HomeComponent implements OnInit { public hxTitle = '我是首页的头部'; con ...