Linux内核源码分析之setup_arch (三)
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 (三)的更多相关文章
- Linux内核源码分析之setup_arch (四)
前言 Linux内核源码分析之setup_arch (三) 基本上把setup_arch主要的函数都分析了,由于距离上一篇时间比较久了,所以这里重新贴一下大致的流程图,本文主要分析的是bootmem_ ...
- Linux内核源码分析之setup_arch (二)
1. 概述 接着上一篇<Linux内核源码分析之setup_arch (一)>继续分析,本文首先分析arm_memblock_init函数,然后分析内核启动阶段的是如何进行内存管理的. 2 ...
- Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.c ...
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...
- Linux内核源码分析 day01——内存寻址
前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...
- Linux内核源码分析方法_转
Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...
- Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...
- Linux内核源码分析方法
一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...
- linux中断源码分析 - 中断发生(三)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 回顾 上篇文章linux中断源码分析 - 初始化(二)已经描述了中断描述符表和中断描述符数组的初始化,由于在初始 ...
随机推荐
- TensorFlow安装方法:附带坑解决办法
>>添加Anaconda 仓库的镜像 Anaconda 安装包可以到 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 下载. ...
- 利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果
利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果 前言 近日公司接到一个轨道系统的需求,需要将地铁线路及列车实时位置展示在大屏上.既然是大屏项目,那视觉效果当然是第一重点,咱们可以先来看看项 ...
- PyQt(Python+Qt)学习随笔:Qt Designer中部件的调色板palette属性和字体font属性设置
一.调色板 在Qt Designer的部件属性中,有个部件调色板(palette)的属性,进入后,如下图所示: 1.调色板palette Qt中提供的调色板palette用于管理控件的外观显示,对应P ...
- 爬虫模块-requests
title: python爬虫01 date: 2020-03-08 22:56:12 tags: 1.requests模块 requests模块的底层是urllib,但是比urllib更强大也更加简 ...
- 《30天自制操作系统》软盘 -> VMware虚拟机
书名叫做30天自制操作系统,按照学校课设答辩的时间来看,估计得把书名改成<一周自制操作系统>,太卷了哈哈哈 我们可以使用qemu来模拟物理机 make run第二天制作的操作系统 可以看到 ...
- Gif开发笔记(一):gif介绍、编译和工程模板
前言 实现gif图片的解码和生成. Gif 简介 GIF格式的名称是Graphics Interchange Format的缩写,是在1987年由Compu Serve公司为了填补跨平 ...
- Scrum 冲刺 第三篇
Scrum 冲刺 第三篇 每日会议照片 昨天已完成工作 队员 昨日完成任务 黄梓浩 继续完成app项目架构搭建 黄清山 完成部分个人界面模块数据库的接口 邓富荣 完成部分后台首页模块数据库的接口 钟俊 ...
- MySQL存储引擎:MyISAM和InnoDB的区别
MyISAM和InnoDB的区别 定义 InnoDB:MySQL默认的事务型引擎,也是最重要和使用最广泛的存储引擎.它被设计成为大量的短期事务,短期事务大部分情况下是正常提交的,很少被回滚.InnoD ...
- 密码学系列之:明文攻击和Bletchley Park
目录 简介 crib和明文攻击 布莱奇利公园(Bletchley Park) 简介 明文攻击就是指已经知道了部分明文和它对应的加密后的字段,从而可以推测出使用的加密手段或者密码本.明文攻击这个故事还要 ...
- Angular:组件之间的通信@Input、@Output和ViewChild
①父组件给子组件传值 1.父组件: ts: export class HomeComponent implements OnInit { public hxTitle = '我是首页的头部'; con ...