Linux内核--读书笔记之内存寻址
【8】linux中的内存管理

4、物理地址、虚拟地址及线性地址 物理地址、 几个地址名词
物理地址: 物理地址:将主板上的物理内存条所提供的内存空间定 义为物理内存空间 物理内存空间, 义为物理内存空间,其中每个内存单元的实际地址就是 物理地址。 物理地址。 虚拟地址: 虚拟地址:将应用程序员看到的内存空间定义为虚拟地 址空间(或地址空间 其中的地址就叫虚拟地址 或地址空间), 虚拟地址(或虚地 址空间 或地址空间 ,其中的地址就叫虚拟地址 或虚地 偏移量” 址),分段机制下,一般用“段:偏移量”的形式来描 ,分段机制下,一般用“ 述。 线性地址:指一段连续的,范围为0到 的地址空间 的地址空间, 线性地址:指一段连续的,范围为 到n的地址空间,一 个线性地址就是线性地址空间的一个绝对地址。 个线性地址就是线性地址空间的一个绝对地址。
2
关于内核源码学习
系统实现的具体细节最终落到源代码上。从理解运行机制出发, 系统实现的具体细节最终落到源代码上。从理解运行机制出发,分 析各种数据结构。在理论明了、有一定的程序基础后再阅读梳理代码, 析各种数据结构。在理论明了、有一定的程序基础后再阅读梳理代码,这 是需要反复且花功夫的一件事。 是需要反复且花功夫的一件事。 阅读工具 帮助追踪复制调用, 帮助追踪复制调用,数据结构定义等 windows环境下利用 环境下利用Source windows环境下利用Source Insight linux环境下利用 环境下利用lxr(linux reference)或glimpse等 linux环境下利用lxr(linux cross reference)或glimpse等 阅读顺序 一般按顺序阅读启动代码;然后进行专题阅读,如进程部分, 一般按顺序阅读启动代码;然后进行专题阅读,如进程部分,内存 管理部分等。在每个功能函数内部应该一步步来。OK,感兴趣的话, 管理部分等。在每个功能函数内部应该一步步来。OK,感兴趣的话,反复 读。 纵向:顺着程序的执行顺序逐步进行。 纵向:顺着程序的执行顺序逐步进行。 横向: 横向:分模块进行 划分不是绝对的, 划分不是绝对的,而是经常结合在一起进行 Linux的启动 顺着linux的启动顺序读,大致流程如下( X86平台 的启动: linux的启动顺序读 Linux的启动:顺着linux的启动顺序读,大致流程如下(以X86平台 为例): 为例): ./arch/x86/boot/bootSect.S ./arch/x86/boot/setup.S ./arch/x86/kernel/head.S ./init/main.c中的 中的start_kernel() ./init/main.c中的start_kernel() 对于内存管理等部分,可以单独拿出来按模块进行阅读分析。 对于内存管理等部分,可以单独拿出来按模块进行阅读分析。 参考书——《LINUX内核源代码情景分析》 《LINUX内核源代码情景分析》 参考书 内核源代码情景分析
linux内存管理子系统 笔记

4-4 linux内存管理子系统4-4-1 linux内存管理(参考课件)物理地址:cpu地址总线上寻址物理内存的地址信号,是地址变换的最终结果逻辑地址:程序代码经过编译后,出现在汇编程序中的地址(程序设计时使用的地址)线性地址:又名虚拟地址,32位cpu架构下4G地址空间CPU要将一个逻辑地址转换为物理地址,需要两步:1、首先CPU利用段式内存管理单元,将逻辑地址转换成线性地址;2、再利用页式内存管理单元,把线性地址最终转换为物理地址相关公式:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)(通用的)16位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)线性地址=段寄存器的值×16+逻辑地址的偏移部分物理地址=线性地址(没有页式管理)32位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)线性地址=段寄存器的值+逻辑地址的偏移部分物理地址<——>线性地址(mapping转换)ARM32位:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)逻辑地址=段内偏移量(段基地址为0)线性地址=逻辑地址=段内偏移量(32位不用乘以32)物理地址<——>线性地址(mapping转换)************************!!以下都是x86模式下!!*********************************一、段式管理1.1、16位CPU:(没有页式管理)1.1.1、段式管理的由来:16位CPU内部有20位地址总线,可寻址2的20次方即1M的内存空间,但16位CPU 只有16位的寄存器,因此只能访问2的16次方即64K。
因此就采用了内存分段的管理模式,在CPU内部加入了段寄存器,这样1M被分成若干个逻辑段,每个逻辑段的要求如下:1、逻辑段的起始地址(段地址)必须是16的整数倍,即最后4个二进制位须全是0 (因此不必保存)。
深入理解Linux内核第二章内存寻址

深⼊理解Linux内核第⼆章内存寻址内存地址 当使⽤80x86微处理器时,必须区分以下三种不同的地址: 1)逻辑地址(logical address),每⼀个逻辑地址都由⼀个段(segment)和偏移量(offset或者displacement)组成,偏移量指明了从段开始的地⽅到实际地址之间的距离。
2)线性地址(linear address),也称虚拟地址(virtual address),是⼀个32bit⽆符整数,可以⽤来表⽰4G的地址,通常由16制数字表⽰。
3)物理地址(physical address),⽤于内存芯⽚级内存单元寻址,他们与从微处理器的地址引脚范松到内存总线上的电信号相对应,物理地址由32bit或36bit⽆符号整数表⽰。
逻辑地址——>【分段单元】——>线性地址——>【分页单元】——>物理地址硬件中的分段 段选择符(Segment Selector)和段寄存器 逻辑地址:段选择符(Segment Selector)和段内偏移量,前者16bit,后者32bit。
段选择符:16bit中,⾼13bit为索引号(可以标识8K,所以⼀个GDT或者LDT中可以放8K个段描述符),然后接着的是1bit是TI(表指⽰器,指⽰是在GDT中还是LDT中),然后是2bit的RPL(请求者特权级) 段寄存器:有6个段寄存器,分别为:cs(代码段寄存器)、ss(栈段寄存器)、ds(数据段寄存器)、es、fs和gs,⽤于存放段选择符。
段描述符符(Segment Descriptor) 每个段描述符(segment descriptor)为8Byte(是字节不是位),描述段的特征。
放在全局描述符表(Global Descriptor Table,GDT)或者局部描述符表(Local Descriptor Table,LDT)中。
通常GDT在系统中只定义⼀个,LDT不⽌⼀个,每个进程都可以有⾃⼰的LDT(可以理解为GDT为⼀级段描述符表,LDT为⼆级描述符表,LDT嵌套在GDT中)。
Linux内存寻址和内存管理

Linux内存寻址和内存管理
1. x86的物理地址空间布局
以x86_32,4G RAM为例:
物理地址空间的顶部以下一段空间,被PCI设备的I/O内存映射占据,它们的大小和布局由PCI规范所决定。
640K~1M这段地址空间被BIOS和VGA适配器所占据。
由于这两段地址空间的存在,导致相应的RAM空间不能被CPU所寻址(当CPU访问该段地址时,北桥会自动将目的物理地址“路由”到相应的I/O设备上,不会发送给RAM),从而形成RAM空洞。
当开启分段分页机制时,典型的x86寻址过程为
内存寻址的工作是由Linux内核和MMU共同完成的,其中Linux内核负责cr3,gdtr等寄存器的设置,页表的维护,页面的管理,MMU则进行具体的映射工作。
2. Linux的内存管理
Linux采用了分页的内存管理机制。
由于x86体系的分页机制是基于分段机制的,因此,为了使用分页机制,分段机制是无法避免的。
为了降低复杂性,Linux内核将所有段的基址都设为0,段限长设为4G,只是在段类型和段访问权限上有所区分,并且Linux内核和所有进程共享1个GDT,不使用LDT(即系统中所有的段描述符都保存在同一个GDT中),这是为了应付CPU的分段机制所能做的最少工作。
Linux内存管理机制可以分为3个层次,从下而上依次为物理内存的管理、页表的管理、虚拟内存的管理。
3. 页表管理
为了保持兼容性,Linux最多支持4级页表,而在x86上,实际只用了其中的2级页表,即PGD(页全局目录表)和PT(页表),中间的PUD和PMD所占的位长都是0,因此对于x86的MMU是不可见的。
内存寻址原理范文

内存寻址原理范文
内存寻址是指CPU在读取或写入内存中的数据时,根据一定的地址去
寻址内存位置的过程。
内存寻址方式可以分为几种,主要有直接寻址、寄存器寻址和间接寻
址等。
1.直接寻址
直接寻址是最简单的寻址方式,它指的是用指令中的地址作为内存存
储单元的地址来进行存取操作。
直接寻址指令最常用的是用来读写内存单
元的指令,如LOAD、STORE、FETCH等。
2.寄存器寻址
寄存器寻址是一种将指令中的地址放入寄存器来存取内存单元的寻址
方式,此时地址不是存放在指令中,而是存放在寄存器中,当指令要操作
内存单元时,则使用寄存器中的地址进行操作。
3.间接寻址
间接寻址是一种比较常用的寻址方式,它指的是将地址存放在内存单
元中,而指令中只存放指向内存单元的指针,当要操作内存单元时,则从
指令中取出指针,根据指针指向的地址来操作内存单元。
4.基址寻址
基址寻址是一种把绝对地址分解成两部分来实现寻址的方式。
其中一
部分存放在指令中,而另一部分则存放在寄存器中。
当指令要操作内存单
元时,则使用这两部分地址拼接成一个完整的绝对地址,来操作内存单元。
5.索引寻址
索引寻址是一种把指令中的地址和寄存器中的地址相加来实现寻址的方式。
Linux内存管理学习笔记

Linux内存管理学习笔记⼀.内存 内存⼜称为主存,是CPU能够直接寻址的存储空间,由半导体制成。
内存的特点是存取速度快。
计算机中所有程序的运⾏都是在内存中进⾏的,因此内存的性能对计算机的影响⾮常⼤。
⼆.物理内存和虚拟内存1. 物理内存:在应⽤中,真实地插在主板内存槽上的内存条。
从本质上来说,物理内存是代码和数据在其中运⾏的窗⼝。
2. 虚拟内存:使程序认为它拥有的连续的可⽤的内存(⼀个连续完整的内存空间),⽽实际上,它通常是被分割成多个物理内存碎⽚,还有部分暂时存储在外部磁盘存储器上,在需要时进⾏数据交换。
三. 为什么要使⽤虚拟内存1. 在早期的计算机中,要运⾏⼀个程序,是会将这些程序全部装⼊内存,程序都是在内存上运⾏的,也就是说程序中访问的内存地址都是实际的物理内存地址。
当计算机同时运⾏多个程序时,必须保证这些程序应⽤到的内存总量要⼩于计算机实际物理内存的⼤⼩。
但是这种简单的内存分配策略问题很多:问题1:进程地址空间不隔离,没有权限保护。
由于程序都是直接访问物理内存,所以⼀个进程可以修改其他进程的内存数据,甚⾄修改内存地址空间中的数据; 问题2:内存使⽤效率低。
当内存空间不⾜时,要将其他程序暂时拷贝到硬盘,然后将新的程序装⼊到内存中运⾏。
由于⼤量的数据装⼊装出,内存使⽤效率会⼗分低下; 问题3:程序运⾏的地址不确定。
因为内存地址是随机分配的,所以程序运⾏的地址也是不确定的。
2.为了解决上述问题,⼈们想到了⼀种变通的⽅法,就会增加⼀个中间层,利⽤⼀种间接的地址访问⽅法访问物理内存。
按照这种⽅法,程序中访问的内存地址不再是实际的物理内存地址,⽽是⼀个虚拟地址,让由操作系统将这个虚拟地址映射到适当的物理内存地址上。
这样只要操作系统处理虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有成蝶,就可以达到内存地址空间隔离的效果。
四. 虚拟内存的实现1. 每个进程都有独⽴的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址。
6.Linux内存管理-内存寻址
21
上面我们介绍了段控制单元模型,有点复杂吧☺ 那么在Linux环境下是怎么样分段的呢 ? Linux 对这个模型稍微进行了修改, 以一种受限的 方法来使用这种分段模型(主要是出于兼容性方 面的考虑)。 在 Linux 中,所有的段寄存器都指向相同的段地 址范围 —— 换言之,每个段寄存器都使用相同的 线性地址。这使 Linux 所用的段描述符数量受限, 从而可将所有描述符都保存在 GDT 之中。这种 模型有两个优点: 当所有的进程都使用相同的段寄存器值时(当它 们共享相同的线性地址空间时),内存管理更为
A=0:尚未被访问 A=1:已被访问 说明
10
• 当把描述符的相应选择子装入到段寄存器时,80386把该位置为1, 表明描述符已被访问。操作系统可测试访问位,已确定描述符是否
数据段描述符说明
Type字段中的E=0
第2位:扩展方向位ED
ED=0:向地址增大的方向扩展,段内偏移必须小于等于 段界限 ED=1:向地址减少的方向扩展,段内偏移必须大于段界 限
LDT段
所有进程共享默认 LDT 段。默认情况下, 其中会包含一个空的段描述符。这个默认 LDT 段描述符存储在 GDT 中。Linux 所生 成的 LDT 的大小是 24 个字节。默认有 3 个条目:
LDT[0] = 空 LDT[1] = 用户代码段 LDT[2] = 用户数据/堆栈段描述符
28
目录(directory):最高的10位,决定页目录项 (指向适当的页表) 页表(table):中间的10位,决定页表项(指向所 在页框的物理地址) 偏移量(offset):最低的12位,决定页框内的相对 位置
线性地址的转换(二级模式)
分两步完成,每一步都基于一种转换表
第一种称为页目录表(page directory)
linux内核注释笔记
任务状态段TSS用于任务在切换时CPU 自动保存与恢复相关任务的执行上下文。
实模式是原来486及以下的机器上,运行DOS系统时的方式。
此时一个程序只要运行就可以对内存中所有的数据段和代码段具有运行和修改的权利,程序是按段地址的方式进行访问的。
此时,内存的地址长度为16位(实际为20位),
保护模式是奔腾及以上的机器上运行的Windows 95及以上的系统时的内存方式,在Win 系统中系统具有ring 0 和ring 3两种运行模式系统的内核运行在ring 0模式下,用户程序运行在ring 3模式下,用户只拥有局部描述符表,只对局部描述符所规定的代码和数据拥有修改和运行的权利(当然,局部描述符表要规定允许修改才能修改。
)只有系统才拥有全局描述符表,才能对整个内存的代码和数据有修改和运行的权利,程序是按段页式方式进行访问的,此时,内存的地址长度是全32位的
实模式:
内存寻址方式为:段式寻址,即物理地址=段地址*16 + 段内偏移地址
可寻址任意地址,所有指令都相当于工作在特权级。
dos工作在实模式下
保护模式:
内存寻址方式为:支持内存分页和虚拟内存
支持多任务,可依靠硬件用一条指令即可实现任务切换,不同任务可工作在
不同的优先级下,操作系统工作在最高优先级0上,应用程序则运行在较低优先级
上。
从实模式到保护模式,需要建立GDT、IDT等数据表,然后通过修改控制寄存
器CR0的控制位(位0)来实现。
深入理解Linux内核一、内存寻址
二、。
内存寻址的方式
内存寻址的方式内存寻址的方式是计算机中进行数据存取的基本方法,它决定了计算机如何将数据存储到内存中,并且在需要时如何从内存中读取数据。
本文将介绍几种常见的内存寻址方式。
1. 直接寻址直接寻址是最简单的内存寻址方式,也是最常用的方式之一。
在直接寻址中,CPU通过将数据的地址直接传递给内存控制器,从而实现对内存的读取或写入操作。
这种方式的优点是速度快,但缺点是地址空间有限,无法处理大于地址空间的数据。
2. 间接寻址间接寻址是通过使用一个指针或地址寄存器来间接访问内存中的数据。
CPU首先从指针或地址寄存器中读取一个内存地址,然后再通过该地址去访问内存中的数据。
这种方式的优点是可以间接地访问内存中的数据,适用于处理复杂的数据结构,但缺点是需要多次访问内存,速度相对较慢。
3. 寄存器间接寻址寄存器间接寻址是一种特殊的间接寻址方式,它使用一个寄存器来存储内存地址。
CPU首先从寄存器中读取一个地址,然后再通过该地址去访问内存中的数据。
这种方式的优点是速度快,但缺点是寄存器的数量有限,无法处理大量的地址。
4. 基址寻址基址寻址是一种常见的寻址方式,它使用一个基址寄存器来存储内存地址的起始位置。
CPU通过将基址寄存器中的值与偏移量相加,得到实际的内存地址。
这种方式的优点是可以处理大量的地址,适用于处理数组或数据结构,但缺点是需要多次计算地址,速度相对较慢。
5. 变址寻址变址寻址是一种常见的寻址方式,它使用一个变址寄存器来存储内存地址的偏移量。
CPU通过将变址寄存器中的值与基址相加,得到实际的内存地址。
这种方式的优点是可以处理不同偏移量的地址,适用于处理多维数组或数据结构,但缺点是需要多次计算地址,速度相对较慢。
6. 相对寻址相对寻址是一种常见的寻址方式,它使用一个相对地址来访问内存中的数据。
CPU通过将相对地址与当前指令的地址相加,得到实际的内存地址。
这种方式的优点是可以在程序中使用相对地址,简化了程序的编写,但缺点是需要多次计算地址,速度相对较慢。
Linux+Kernel学习笔记
Linux Kernel学习笔记Table of Contents1. 存储器寻址2. 设备驱动程序开发3. 字符设备驱动程序3.1. 设备号3.2. 设备号的分配和释放3.3. 重要的数据结构3.4. 读和写4. PCI设备5. 内核初始化优化宏6. 访问内核参数的接口7. 内核初始化选项8. 内核模块编程8.1. 入门8.2. 为模块添加描述信息8.3. 内核模块处理命令介绍9. 网络子系统9.1. sk_buff结构9.2. sk_buff结构操作函数9.3. net_device结构9.4. 网络设备初始化9.5. 网络设备与内核的沟通方式9.6. 网络设备操作层的初始化9.7. 内核模块加载器9.8. 虚拟设备9.9. 8139too.c源码分析9.10. 内核网络数据流10. 备忘录Chapter 1. 存储器寻址在80x86微处理器中,有三种存储器地址:逻辑地址(logical address),包含在机器语言指令中用来指定一个操作数或一条指令的地址。
每个逻辑地址都由一个段(segment)和一个偏移量(offset)组成。
偏移量指明了从段的开始到实际地址之间的距离。
∙线性地址(linear address)(也称为虚拟地址,virtual address),它是一个32位无符号整数,可用以表达高达4G的地址(2的32次方)。
通常以十六进制数表示,值的范围从0X00000000到0Xffffffff。
∙物理地址(physical address),用于存储器芯片级存储单元寻址,它们与从微处理器的地址引脚发送到存储器总线上的电信号相对应。
物理地址由32位无符号整数表示。
CPU控制单元通过一种称为分段单元(segmentation unit)的硬件电路把一个逻辑地址转换成线性地址;线性地址又通过一个分页单元(paging unit)的硬件电路把一个线性地址转换成物理地址。
逻辑地址由两部份组成,一个段标识符和一个指定段由相对地址的偏移量。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
《深入理解Linux内核》读书笔记
-----内存寻址
题记: 最近在看《深入理解linux内核》这本书,看的时候记了些笔记,主要是书上一些重要知识点的謫录还有就是自己的一些个人理解,其实一些地方我也没大看懂,还需要继续研究,先把东西记在这,一些地方记的也有点乱,主要是为以后自己再回去学习这方面知识做一些线索指导,也希望对大家学习这方面只是有些帮助,有什么不正确的地方还请指正
三种地址
逻辑地址(logical address):
包含机器语言指令中用来指定一个操作数或一条指令的地址
每一个逻辑地址都由一个段(segment)和偏移量(offset)组成
线性地址(又称虚拟地址):
是一个32位的无符号整数,可以用来表示高达4G的地址
通常用十六进制数字表示,值的范围从0x00000000到0xffffffff
物理地址:
与从微处理器的地址引脚发送到内存总线上的电信号有关
内存寻址的转换过程:
逻辑地址---(分段单元)--->线性地址---(分页单元)--->物理地址
硬件分段:
GDT:全局描述符LDT:局部描述符
逻辑地址由一个16位长的段选择符和32位的偏移量组成
为了快速方便的找到段选择符,处理器提供了段寄存器,段寄存器的
目的是存放段选择符
每个段由1个8字节的段描述符表示,它表述了段的特征,存放在GDT或LDT中
我们在寻址的时候,一般是从段选择符找到段描述符,然后从段描述符中取得段基址
加上偏移量就形成了我们要访问的地址
段选择符(16位)用来表示指向哪个段描述符,即用来在段描述符表(GDT,LDT)中寻址,
前13位是地址,能寻从0到2^13-1,因此段描述符表的大小是8192,后三位是一些特
权级的限制
段描述符是用来表示这个段的一些属性,如段基址和段长之类的
段选择符的后三位:index,TI,RPL
逻辑机制转换成线性地址的流程:
1)先检查段选择符的TI字段,以决定段描述符保存在哪一个描述符表中。
TI字段(0或1)指明描述符是在GDT中还是在LDT中
2)从段选择符的index字段计算段描述符的地址,index字段的值乘以8(一个段描述符的大小),
这个结果在与gdtr或ldtr寄存器中的内容相加
3)把逻辑地址的偏移量与段描述符Base字段的值相加就得到了线性地址
Linux中的分段
分段可以给每一个进程分配不同的线性地址空间,而分页可以把同一线性地址空间映射到不同的物理空间,与分段相比,linux更喜欢使用分页,因为:
1)当所有进程使用相同的段寄存器值时,内存管理变得更简单,也就是说它们能共享同样的一组线性地址
2)Linux设计目标之一是可以把他移植到绝大多数流行的处理器平台上。
然而,RISC体系结构对分段的支持有限
Linux下所有段都是从0x00000000开始的,所以在linux下逻辑地址和线性地址是一致的(是一致,不是相同),即逻辑地址的偏移量字段的值和相应的线性地址的值是一致的。
硬件分页机制
分页是将线性地址转换成物理地址。
为了效率起见,线性地址被分成以固定长度为单位的组,称为页(page),在一个页面上的连续
的地址也被映射到连续的物理地址上.
分页单元把所有的RAM分成固定程度的页框(也叫物理页),每个页框包含一个页(page)。
页和页框是有区别的,前者只是一个数据块,可以存放在任何页框或磁盘中,而后者是一个存储区域
常规分页:从80386起,Intel处理器的分页单元处理4KB(2^12)的页
32位的线性地址被分成Directory(目录10位),Table(页表,10位),Offset(偏移量12位).线性地址的转换主要就是2种表的转换,页目录表(page directory)和页表(page table).
另外,每个活动的进程必须有一个分配给它的页目录,正在使用的页目录的物理地址存放在控制寄存器的CR3中,也就是说不同的进程CR3的值是不同的。
PAE(物理地址扩展)分页机制和64位的分页,原理上和常规分页是类似的,只是他们通过增加地址线使访问范围要大一些,还有就是一些页表和offset之类的设置不一样,由于用的也不多,我也没咋深入看,感兴趣的话,可以去网上找这方面的资料
为了缩小CPU和RAM之间的速度不匹配,引入了硬件高速缓存内存(hardware cache memory),它是基于著名的局部性原理的
除了硬件高速缓存(cache)外,80x86处理器还包含了另外一个称为转换后援缓冲器(TLB)的高速缓存用于加快线性地址的转换,加速页表的查找。
当一个线性地址被第一次使用时,通过慢速访问的RAM中的页表计算出相应的物理地址。
同时,物理地址被存放在一个TLB 表项中,以便以后对同一个线性地址的引用可以快速的得到转换.
Linux的分页
Linux采用了一种同时适用于32位和64位系统的普通分页模型,在2.6.11之前,采用三级
分页,2.6.11开始采用四级分页模型:页全局目录,页上级目录,页中间目录,页表。
通过设置一些目录是否为全0,来对应32位或是64位系统。
Linux定义了很多宏和函数来做这方面的处理,在这就不多说了,有兴趣的话可以去看原书或是去网上找这方面的资料
物理地址布局:
在初始化阶段,内核必须建立一个物理地址映射来指定哪些物理地址范围对内核来说是可用的。
Linux内核一般被加载在物理地址0x00100000开始的地方,即第二个MB开始的地方,因为之前1MB的空间需要留给BIOS之类的程序。
内核询问BIOS并了解内存的大小。
随后,内核执行machine_specific_memory_setup(void) 函数(include/asm-i386/mach-default/setup_arch_post.h中),该函数建立物理地址映射。
setup_memory()在machine_specific_memory_setup(void)之后被调用,它分析物理内存区域表并初始化一些变量来描述内核的物理内存布局.
进程页表:
进程的线性地址空间被分为两个部分:
从0x00000000到0xbfffffff(3GB)的用户态线性地址,0xc00000000到0xffffffff(1GB)内核态线性地址。
宏PAGE_OFFSET的值是0xc0000000,这就是进程在线性地址空间的偏移量,也是内核空间的开始之处。
内核页表:
内核维持着一组自己使用的页表,驻留在所谓的主内核页全局目录中。
内核在刚刚被装入内存后,CPU仍然运行在实模式,所以分页功能没有被启用。
内核初始化页表分为两个阶段:
第一个阶段:内核创建一个有限的地址空间,包括内核的代码段和数据段,初始化页表和用于动态数据结构的共128KB大小的空间,该空间仅够将内核装入RAM和对其初始化的核心数据结构。
第二个阶段:内核充分利用剩余的RAM并适当的建立分页表。
临时页全局目录是在内核编译过程中静态初始化的,而临时页表是由startup_32()汇编语言函数(arch/i386/kernel/head.S)初始化的,此时的页上级目录和页中间目录相当于页全局目录项
临时页全局目录放在swapper_pg_dir变量中,临时页表放在pg0变量处开始存放,为简单起见,我们假设内核使用的段,临时页表和128KB的内存范围能容纳于RAM的前8M空间里分页第一个阶段的目标是允许在实模式和保护模式下都能很容易的对这8MB寻址,因此,内核创建一个映射,把0x00000000~0x007fffff(8M)和0xc0000000~0xc07fffff(8M)的线性地址映射到0x00000000~0x007fffff的物理地址。
汇编语言函数startup_32()也启用分页单元,通过向cr3控制寄存器装入swapper_pg_dir的地址及设置cr0控制寄存器的PG标志来达到这一目的
对不同RAM的内核页表
1),当RAM小于896MB的时的最终内核页表
(注:这个896MB是用内核空间的1024MB减去128MB得来的,这128MB有别的用处,如非连续空间的物理映射,高端内存页框映射等,在以后的内存管理部分会继续讲)
由内核页表所提供的最终映射必须把从0xc0000000开始的线性地址转换为从0开始的物理地址。
宏__pa用于把从PAGE_OFFSE(0xc0000000)开始的线性地址转换成相应的物理地址,而__va做相反的转化(千万要注意,这个转换只适用于内核空间,用户空间有另外的转换方式)
2),当RAM大小在896MB和4096MB之间时的最终内核页表
这种情况下,并不把RAM全部映射到内核地址空间,Linux在初始化阶段可以做的最好的事是把一个具有896MB的RAM窗口映射到内核线性地址空间,如果一个程序需要对现有RAM的其余地址寻址,那就必须把其他的线性地址间隔映射到所需的RAM(在内存管理部分会继续深入讲解)
3)当RAM大于4096MB时的内核页表
还在看这部分,在平时的应用中很少用到大于4096MB的,主要是需要支持PAE的情况的,PAE处理36位物理地址,其实和之前的差不多,前面的896MB直接映射,剩余的留下来不映射,由动态重映射开处理。
主要是需要支持PAE的情况的,PAE处理36位物理地址寻址空间也要大一些,所以使用的分页方式需要使用三级分页模型。