linux arm mmu基础【转】
转自:http://blog.csdn.net/xiaojsj111/article/details/11065717
ARM MMU页表框架
先上一张arm mmu的页表结构的通用框图(以下的论述都由该图来逐渐展开):
以上是arm的页表框图的典型结构:即是二级页表结构:
其中第一级页表(L1)是由虚拟地址的高12bit(bits[31:20])组成,所以第一级页表有4096个item,每个item占4个字节,所以一级页表的大小为16KB,而在第一级页表中的每个entry的最低2bit可以用来区分具体是什么种类的页表项,2bit可以区分4种页表项,具体每种页表项的结构如下:
简而言之L1页表的页表项主要有两大类:
第一大类是指向第二级页表(L2页表)的基地址;
第二类直接指向1MB的物理内存。
在L1页表中每个表项可以覆盖1MB的内存,由于有4096K个选项(item),所以总计可以覆盖4096K*1MB=4GB的内存空间。
具体对应到linux,由于linux的软件架构是支持3级页表结构,而arm架构实际只有2级的页表结构,所以linux代码中的中间级页表的实现是空的。在linux代码中,第一级的页表的页目录表项用pgd表示,中间级的页表的页目录表项用pud表示(arm架构其实不需要),第三级的页表的页目录表项用pmd表示(由于中间pud是空的,所以pgd=pmd),另外目前arm体系的移动设备中RAM的page大小一般都是4KB/page,所以L1页表中的页表项都是指向fine
page table的。
但在linux内核启动的初始化阶段,临时建立页表(initial
page tables)以供linux内核初始化提供执行环境,这时L1的页表项使用的就是第二种页表项(section
enty),他直接映射的是1M的内存空间。具体的可以参考arch/arm/kernel/head.S中的__create_page_tables函数,限于篇幅,这里就不展开说了。
针对这种section page translation,mmu硬件执行虚拟地址转物理地址的过程如下:
以上在初始化过程使用的临时页表(initial
page
tables),在内核启动的后期会被覆盖掉,即在paging_init--->map_lowmem函数中会重新建立页表,该函数为物理内存从0地址到低端内存(lowmem_limit)建立一个一一映射的映射表。所谓的一一映射就是物理地址和虚拟地址就差一个固定的偏移量,该偏移量一般就是0xc0000000(呵呵,为什么是0xc0000000?)
说到这里引入一个重要的概念,就是与低端内存相对的高端内存,什么是高端内存?为什么需要高端内存?为了解析这个问题,我们假设我们使用的物理内存有2GB大小,另外由于我们内核空间的地址范围是从3G-4G的空间,并且前面也说到了,linux内核的低端内存空间都是一一映射的,如果不引入高端内存这个概念,全部都使用一一映射的方式,那内核只能访问到1GB的物理内存,但实际上,我们是需要内核在内核空间能够访问所有的4GB的内存大小的,那怎么做到呢?
方法就是我们不让3G-4G的空间都使用一一映射,而是将物理地址的[0x00,fix_addr](fix_addr<1GB)映射到内核空间虚拟地址[0x00+3G,fix_addr+3G],然后将[fix_addr+3G,4G]这段空间保留下来用于动态映射,这样我们可以通过这段虚拟地址来访问从fix_addr到4GB的物理内存空间。怎么做到的呢?
譬如我们想要访问物理地址[fix_addr,4GB]这段区间中的任何一段,我就用宝贵的内核虚拟地址[fix_addr+3G,4G]的一段去映射他,建立好mmu硬件使用的页表,访问完后,将映射清除,将内核的这段虚拟地址释放,以供下次访问其他的物理内存使用。这样就可以达到访问所有4GB的物理内存的目的。
那么内核代码是如何建立映射表的呢?
我们着重从arch/arm/mm/mmu.c中的create_mapping函数来分析。在分析之前我们先看下arm mmu硬件是如何在二级页表结构中,实现虚拟地址转物理地址的。
先贴出原代码(arch/arm/mm/mmu.c):
该函数的功能描述如下:
Create the page directory entries and any necessary
page tables for the mapping specified by `md'. We
are able to cope here with varying sizes and address
offsets, and we take full advantage of sections and
supersections.
line737-line742:参数合法性检查,该函数不为用户空间的虚拟地址建立映射表(记得多问自己一个为什么?)
line744-line750:如果是iomemory,则映射的虚拟地址范围应属于高端内存区间,由于我们这里是常规的memory,即type为MT_MEMORY,所以不会进入该分支
line775:
获得该虚拟地址addr属于第一级页表(L1)的哪个表项,详细跟踪pgd_offset_k函数(定义在:arch/arm/include/asm/pgtable.h),你会发现,我们内核的L1页目录表的基地址位于0xc0004000,而我们的内核代码则是放置在0xc0008000开始的位置。而从0xc0004000到0xc0008000区间大小是16KB,刚好就是L1页表的大小(见文章开头的描述)
在这里需要注意一个概念:内核的页目录表项和进程的页目录表项,内核的页目录表项是对系统所有进程都是公共的;而进程的页目录表项则是跟特定进程相关的,每个应用进程都有自己的页目录表项,但各个进程对应的内核空间的页目录表相都是一样的。正是由于每个进程都有自己的页目录表相,所以才能做到每个进程都可以独立拥有属于自己的[0,3GB]的内存空间。
line778 pgd_addr_end()确保[addr,next]地址不会跨越一个L1表项所能映射的最大内存空间2MB(为什么是2MB而不是1MB呢?这个是linux的一个处理技巧,以后再详细展开说)
line780 alloc_init_pud()函数为定位到的L1页目录表项pgd所指向的二级页表(L2)建立映射表
line784 pdg++下移L1页目录表项pgd,映射下一个2MB空间的虚拟地址到对应的2MB的物理空间。
在这里解析下,为什么L1页目录表项pgd能够映射2MB的虚地地址空间。
在本文的第一个图中,他是arm典型的mmu映射框架图,但并不是linux的,linux映射框架图在它的基础做了些调整和优化。
linux所做的调整描述如下(以下摘自linux内核:arch/arm/include/asm/pgtable-2level.h中提供的注释说明):
/*
* Hardware-wise, we have a two level page table structure, where the first
* level has 4096 entries, and the second level has 256 entries. Each entry
* is one 32-bit word. Most of the bits in the second level entry are used
* by hardware, and there aren't any "accessed" and "dirty" bits.
*
* Linux on the other hand has a three level page table structure, which can
* be wrapped to fit a two level page table structure easily - using the PGD
* and PTE only. However, Linux also expects one "PTE" table per page, and
* at least a "dirty" bit.
*
* Therefore, we tweak the implementation slightly - we tell Linux that we
* have 2048 entries in the first level, each of which is 8 bytes (iow, two
* hardware pointers to the second level.) The second level contains two
* hardware PTE tables arranged contiguously, preceded by Linux versions
* which contain the state information Linux needs. We, therefore, end up
* with 512 entries in the "PTE" level.
*
* This leads to the page tables having the following layout:
*
重要调整说明如下:
L1页表从4096个item变为2048个item,但每个item的大小从原来的4字节变为8个字节。
一个page中,放置2个L2页表,每个还是256项,每项是4个字节,所以总计是256*2*4=2KB,放置在page页的下半部,而上部分放置对应的linux内存管理系统使用的页表,mmu硬件是不会去使用它的。所以刚好
占满一个page页的大小(4KB),这样就不浪费空间了。
有了上面基础,下面再详细的分析以上的line780的函数alloc_init_pud,该函数会最终调用到alloc_init_pte函数:
line598 early_pte_alloc函数判断对应的pmd所指向的L2页表是否存在,如果不存在就分配L2页表,如果存在就返回L2页表所在page页的虚地址。
line572
判断pmd所指向的L2页表是否存在,不存在则通过early_alloc
函数分配PTE_HWTABLE_OFF(512*4=2KB)+PTE_HWTABLE_SIZE(512*4=2KB)总计4KB的一个物理页来存储2个linuxpet
页表+2个hwpte页表。
line574返回这个物理页所在虚拟地址
回到alloc_init_pte函数的line599:
line183 pte_index用来确定该虚拟地址在L2页表中的偏移量。即虚拟地址的bit[12~21]共计9个bit,刚好用于寻址两个L2页表(总计512项)
回到alloc_init_pte函数,其中line605行,是设置L2页表中addr所定位到的页表项(即pte),主要工作就是填充对应物理页的物理地址,以供mmu硬件来实现地址的翻译。
line604~line607循环填充完两个hwpte页表,完成一个2M物理内存的映射表的建立。
line608 将最终调用如下函数:static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, pmdval_t prot)
在执行这个函数之前,2个L2页表已经建立,该函数的作用就是设置L1页表的对应表项,使其指向刚建立的2个L2页表(hwpte0,hwpte1),正如前面所说,由于linux的L1页表项是8个字节大小,所以:
line133 将头4个字节指向hwpte0页表,
line135 将后4个字节指向hwpte1页表,至此L1---〉L2页表的关联已经建立。
line137 是刷新TLB缓冲,使系统的cpu都可以看见该映射的变化
至此已完成struct map_desc *md结构体所指定的虚拟地址到物理地址的映射关系的建立,以供硬件mmu来自动实现虚拟到物理地址的翻译。
以上过程,有选择的将某些细节给省略了,限于篇幅,另外如果明白了这个过程,很细节的可以自己去看相关的代码。譬如上面的set_pte_ext函数,会调用的汇编函数来实现pte表项的设置。
linux arm mmu基础【转】的更多相关文章
- 应聘linux/ARM嵌入式开发岗位
**************************************************************** 因为发在中华英才和智联招聘没有人采我所以我 在这里发布我的个人简历希望 ...
- linux arm的高端内存映射
linux arm的高端内存映射(1) vmalloc 高端内存映射 与高端映射对立的是低端映射或所谓直接映射,内核中有关变量定义它们的它们的分界点,全局变量high_memory,该变量定义在m ...
- linux arm的存储分布那些事之一【转】
转自:http://blog.csdn.net/xiaojsj111/article/details/11724081 linux arm的存储分布那些事之一 linux arm 内存分布总览 上图是 ...
- linux arm的存储分布那些事之一
转自:http://blog.csdn.net/xiaojsj111/article/details/11724081 linux arm 内存分布总览 上图是linux的arm的虚拟地址分布 ...
- ARM MMU
关于MMU,以下几篇文章写得通俗易懂: s3c6410_MMU地址映射过程详述 追求卓越之--arm MMU详解 基于S3C6410的ARM11学习(十五) MMU来了 这里总结MMU三大作用: 1. ...
- Linux 脚本编写基础
txt去重 http://man.linuxde.net/sort Linux 脚本编写基础 http://www.cnblogs.com/linn/archive/2007/03/05/664 ...
- linux发行版基础目录
linux发行版基础目录 linux 基础目录 linux基础目录 目录 作用 / 根目录,起源 /boot linux引导启动目录 /lib 库目录 /bin 常用内部命令 /sbin 常用内部管理 ...
- LINUX二十个基础命令
LINUX二十个基础命令 一. useradd命令 1.命令格式: useradd 选项 用户名 2.命令功能: 添加新的用户账号 3.常用参数: -c comment 指定一段注释性描述.-d 目录 ...
- Linux命令工具基础04 磁盘管理
Linux命令工具基础04 磁盘管理 日程磁盘管理中,我们最常用的有查看当前磁盘使用情况,查看当前目录所占大小,以及打包压缩与解压缩: 查看磁盘空间 查看磁盘空间利用大小 df -h -h: huma ...
随机推荐
- 关于BIO和NIO的理解
摘要: 关于BIO和NIO的理解 最近大概看了ZooKeeper和Mina的源码发现都是用Java NIO实现的,所以有必要搞清楚什么是NIO.下面是我结合网络资料自己总结的,为了节约时间图示随便画的 ...
- aliyun阿里云alibabaMaven仓库地址——加速你的maven构建
在maven的settings.xml 文件里 搜索 mirrors ,把下面内容添加在其子节点内 <mirror> <id>nexus-aliyun</id> ...
- 安装和使用 PyInstaller 遇到的问题
写在前面 在学习 Python语言程序设计 的时候,其中有一节课提到了 PyInstaller 第三方库.PyInstaller 可以用来打包 python 应用程序,打包完的程序就可以在没有安装 p ...
- 使用msiexec.exe绕过应用程序白名单(多种方法)
0x00 前言 在我们之前的文章中,我们讨论了“Windows Applocker策略 - 初学者指南”,因为它们为应用程序控制策略定义了AppLocker规则,以及如何使用它们.但今天您将学习如何绕 ...
- android中的style部分属性值介绍 --zz
Android平台定义的主题样式: android:theme="@android:style/Theme.Dialog" 将一个Activity显示为对话框模式 •andro ...
- 【THUSC2017】杜老师
题目描述 杜老师可是要打+∞年World Final的男人,虽然规则不允许,但是可以改啊! 但是今年WF跟THUSC的时间这么近,所以他造了一个idea就扔下不管了…… 给定L,R,求从L到R的这R− ...
- CentOS 7安装zabbix-2.4.8监控
说明: 操作系统:CentOS 7 Web环境:Nginx+MySQL+PHP zabbix版本:zabbix-2.4.8.tar.gz 备注:Linux下安装zabbix需要有LAMP或者LNMP运 ...
- 2018 ACM-ICPC 中国大学生程序设计竞赛暨丝绸之路程序设计竞赛
三道大水题,其它题都不会做,真是尴尬和无奈啊…… 有想法,但是解决不了,感觉个人不会一些基本解法,终究还是个人学习的内容太少了 B. Goldbach /* 数值较小,<2^63,分解的两个素数 ...
- BZOJ1053:反素数(数学)
题目链接 对于任意的正整数\(x\),记其约数的个数为\(g(x)\).现在定义反素数:对于\(0<i<x\),都有\(g(x)>g(i)\),那么就称x为反素数. 现在给定一个数N ...
- 【Asp.net入门07】第一个ASP.NET 应用程序-创建数据模型和存储库
1.理解概念 先理解一下两个概念. 模型 模型是指数据的结构类型,以及可调用的方法.对面向对象编程方法来说,其实就是类.模型类就是一个描述数据的类.只有把数据按一定方式描述出来,我们才能在程序中方便地 ...