Linux内核剖析 之 内存寻址(一)

1、内存地址

逻辑地址(Logical Address):包含在机器语言中用来指定一个操作数或一条指令的地址。每个逻辑地址都由一个端(segment)和偏移量(offset或displacement)组成,偏移量指明了从段开始的地方到实际地址之间的距离。

线性地址(Linear Address)(也称虚拟地址 Virtual Address:是一个32位无符号整数,可以用来表示高达4GB的地址。线性地址通常用十六进制数字来表示,值的范围从0x00000000到0xffffffff。

物理地址(Physical Address:用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32为或36位无符号整数表示。

内存条硬件图:

 

内存芯片寻址的基本原理:

在存储器中保存着大量数据。为了方便保存和提取这些数据,必须为这些数据编上号。但是,这个序号不是按系列顺序编址的,而是按行和列的顺序编址的。因此,当提取它们的时侯,只要指明行数(称行址)和列数(称列址)就可以很轻易地寻找到它们。这就是内存芯片寻址的基本原理。内存芯片的内部结构就相当是一张大的矩阵网,由行(Row)线和列(Column)线相互交叉得到一系列单元格(cell),数据都被依序放在单元格中。我们把矩阵中的每个单元格称为单元(Cell),它是内存中存储数据的最小逻辑单位。把这个大矩阵就称为逻辑存储块(Logical
Bank),简写为L-Bank。

 内存条抽象图:

内存控制单元(存储器管理单元 MMU)通过分段单元(Segmentation Unit)的硬件电路把一个逻辑地址转换为线性地址,接着,分页单元(Paging Unit)硬件电路把线性地址转换为物理地址。

 

2、逻辑地址->线性地址(分段:硬件+Linux)

2.1 段选择符与段寄存器

逻辑地址:段选择符(Segment Selector,16位)+段内偏移(Offset,32位)

Index:在GDT或LDT中段描述符的位置。

TI:段描述符在GDT中(TI=0),段描述符在LDT中(TI=1)。

RPL:请求者特权级,当段选择符装入cs寄存器中,指示CPU的当前特权级。

 

为了方便的找到段选择符,处理器提供六个段寄存器存放段选择符。分别是:

cs ss ds es fs gs

其中,

cs:代码段寄存器,指向包含程序指令的段。

ss:栈段寄存器,指向包含当前程序栈的段。

ds:数据段寄存器,指向包含静态数或者全局数据段。

其他三个段寄存器作一般用途,可以指向任意的数据段。

cs寄存器的RPL字段(两位字段)表示CPU的当前特权级(CPL),内核态0,用户态3

2.2 段描述符(Segment Descriptor) 

每个段由一个8字节的段描述符表示,它描述了段的特征。

全局描述符表(GDT)和局部描述符表(LDT)用于存放段描述符。

通常只定义一个GDT,而每个进程除了存放在GDT中的段之外,如果还需要创建附加的段,就可以有自己的LDT。

GDT在主存中的位置和大小存放在gdtr控制寄存器中,当前正在被使用的LDT地址和大小放在ldtr寄存器中。

段描述符关键字段:

Base: 段基地址

G :粒度标志:清0,段大小以字节为单位,否则以4096字节为单位。

Limit:存放段中最后一个内存单元的偏移量,即段长度。

S:系统标志,0:系统段,存储诸如LDT之类关键数据结构;否则,普通代码段或数据段。

Type:描述了段的类型和它的存取权限。

DPL:描述符特权级(Descriptor Privilege Level)字段,用于存取这个段都要求的特权级,表示为访问这个段而要求的CPU最小的优先级。

段的类型及对应段描述符:

代码段描述符:

表示这个段描述符代表一个代码段,它可以放在GDT或LDT中。该描述符置S标识为1(非系统段)。

数据段描述符:

表示这个段描述符代表一个数据段,它可以放在GDT或LDT中。该描述符置S标识为1,栈段是通过一般的数据段实现的。

任务状态段描述符(TSSD):

表示这个段描述符代表一个任务状态段(Task State Segment,TSS)。也就是说这个段用于保存处理器寄存器的内容。它只能出现在GD中。该描述符置S标志为0(系统段)。

局部描述符表描述符(LDTD)

表示这个段描述符代表一个包含LDT的段,它只出现在GDT中,相应的Type字段的值为2,S标志置为0(系统段)。

结构:

 

 

2.3 逻辑地址的转换(分段单元:逻辑地址 —> 线性地址)

具体操作:

*检查段选择符的TI字段,确定段描述符保存在哪一个描述表中。TI字段指明描述符是在GDT中还是在激活的LDT中,分段单元根据TI字段从gdtr或ldtr寄存器中获得GDT或LDT的线性基地址。

*从段选择符的Index字段计算出段描述符的地址,Index字段的值乘以8(一个段描述符的大小),此结果与gdtr或ldtr寄存器中的内容相加。

*把逻辑地址的偏移量与段描述符Base字段的值相加,即得到线性地址。

3、Linux中的分段

3.1 Linux中的分段

Linux以非常有限的方式使用分段。

四个主要的linux段的段描述符:



Base


G


Limit


S


Type


DPL


D/B


P


用户代码段


0x00000000


1


0xfffff


1


10


3


1


1


用户数据段


0x00000000


1


0xfffff


1


2


3


1


1


内核代码段


0x00000000


1


0xfffff


1


10


0


1


1


内核数据段


0x00000000


1


0xfffff


1


2


0


1


1

相应的段选择符由宏__USER_CS,__USER_DS,__KERNEL_CS,__KERNEL_DS分别定义。

例如,为了对内核代码段寻址,只需要将__KERNEL_CS宏产生的值装入cs寄存器中即可。

所有段的段基地址都为0x00000000,地址空间都是从0~232-1。

可就是,Linux下逻辑地址与线性地址是一致的,逻辑地址的偏移量字段与相应线性地址的值总是一一对应的。

3.2 Linux GDT

一个CPU对应一个GDT,所有的GDT都存放在cpu_gdt_table数组中。

所有GDT的地址和它们的大小被存放在cpu_gdt_descr数组中。

LinuxGDT:


Linux全局描述符表


段选择符


Linux全局描述符表


段选择符


Null


0x0


TSS


0x80


Reserved


 


LDT


0x88


Reserved


 


PNPBIOS 32-bit Code


0x90


Reserved


 


PNPBIOS 16-bit Code


0x98


Reserved


 


PNPBIOS 16-bit Data


0xa0


Reserved


 


PNPBIOS 16-bit Data


0xa8


TLS #1


0x33


PNPBIOS 16-bit Data


0xb0


TLS #2


0x3b


APMBIOS 32-bit Code


0xb8


TLS #3


0x43


APMBIOS 16-bit Code


0xc0


Reserved


 


APMBIOS Data


0xc8


Reserved


 


Not Used


 


Reserved


 


Not Used


 


Kernel Code


 


Not Used


 


Kernel Data


 


Not Used


 


User Code


 


Not Used


 


User Data


 


Double Fault TSS


0xf8

每个GDT包含18个段描述符和14个空的,未使用的或保留的项。插入未使用的项的目的是为了使经常一起访问的描述符能够处于同一个32字节的硬件高速缓冲行中。

每一个GDT中包含的18个段描述符指向下列的段:

*3个局部线程存储段(Thread-Local Storage,TLS):线程私有数据,系统调用set_thread_area()和get_thread_area()分别为正在执行的进程创建和撤销一个TLS段。

*4个用户态和内核态下的代码段,数据段

*TSS:任务状态段,每个CPU一个。所有的任务状态段都顺序存放在init_tss数组中。

       
#第n个CPU的TSS描述符的Base字段指向init_tss数组的第n个元素。

       
#G标志清0,Limit为0xeb,也就是段长为236字节。Type字段置为9或11。DPL为0,不允许用户态访问。

       
#进程切换,进程上下文切换时,这个段用于保存CPU寄存器的内容。

*LDT段:一般指向包含缺省LDT表的段。(大多数用户态下程序都不使用LDT,所以定义一个缺省的LDT供大多数进程共享)。

        #缺省的局部描述符表存放在default_ldt数组中,它包含5个项,但内核仅仅有效地使用了其中的两个项(用于iBCS执行文件的调用门和Solaris/x86可执行文件的调用门)。modify_ldt()系统调用允许进程创建自己的局部描述符表(例如Wine程序),此时LDT段相应的被修改。

*Double Fault TSS处理双重错误异常的特殊的TSS段。

*3个与高级电源管理(AMP)有关的段。

*5个与支持PnP功能的BIOS服务有关的段。

时间: 2024-09-20 00:08:11

Linux内核剖析 之 内存寻址(一)的相关文章

Linux内核剖析 之 内存寻址(三)

//接前一篇博客: 本节主要讲述Linux中的分页机制,注意对比Linux和80x86下分页机制的不同. 5.Linux中的分页                  图:Linux分页模式(四级页表)        Linux的进程处理很大程度上依赖于分页,每个进程都有自己的页全局目录和自己的页表集. 5.1.线性地址字段:       下列宏简化了页表处理.(80x86)       PAGE_SHIFT:       指定Offset字段的位数:当用于80x86处理器时,其值为12.     

Linux内核剖析 之 内存寻址(二)

//接前一篇博客: 本节主要介绍硬件中分页机制. 4.线性地址->物理地址(分页:硬件部分)        为了效率,线性地址被分成以固定长度为单位的组,称为页(Page),页内部连续的线性地址被映射到连续的物理地址中.       分页单元把所有的内存分成固定长度的页框(page frame).每个页框包含一个页(page),也就是说一个页框的长度与一个页的长度一致.页框是主存的一部分,因此是一个存储区域.页和页框是不同的,也只是一个数据块,可以存放在任何页框或磁盘中. 4.1.分页     

Linux内核剖析 之 内存管理

1. 内存管理区     为什么分成不同的内存管理区?     ISA总线的DMA处理器有严格的限制:只能对物理内存前16M寻址.     内核线性地址空间只有1G,CPU不能直接访问所有的物理内存.     ZONE_DMA                  小于16M内存页框     ZONE_NORMAL          16M~896M内存页框     ZONE_HIGHMEM        大于896M内存页框     ZONE_DMA和ZONE_NORMAL区域包含的页框,通过线性

Linux内核剖析 之 历史和体系结构分析

Linux 内核剖析 之 历史和体系结构分析       Linux 内核是一个庞大而复杂的操作系统的核心,不过尽管庞大,但是却采用子系统和分层的概念很好地进行了组织.在本文中,您将探索 Linux 内核的总体结构,并学习一些主要的子系统和核心接口.您还可以通过其他 IBM 文章的链接更深入地进行学习.       由于本文的目标是对 Linux 内核进行介绍并探索其体系结构和主要组件,因此首先回顾一下 Linux 的简短历史,然后从较高的层次审视 Linux 内核的体系结构,最后介绍它的主要子

Linux内核剖析 之 Linux源代码组成

Linux内核剖析 之 Linux源代码组成   ++++Linux:           ++COPYING:有关公共许可证制度GPL的具体说明.           ++README:Linux内核安装和使用的简要说明.           ++Makefile:重构Linux内核可执行代码的make文件.用来组织内核的各模块,记录了个模块间的联系和依托关系,编译时使用:仔细阅读各子目录下的Makefile文件对弄清各个文件这间的联系和依托关系很有帮助.           ++CREDIT

Linux内核中的内存管理浅谈

 [十月往昔]--Linux内核中的内存管理浅谈 为什么要叫做"十月往昔"呢?是为了纪念我的原博客. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月里的文档. 十月往昔,总有一些觉得珍贵的,所以搬迁到这里来. 而这篇文章是在09.04.20-09.04.21里写的. Jason Lee   ------------–cut-line   1.基本框架(此处主要谈页式内存管理) 4G是一个比较敏感的字眼,早些日子,大多数机器(或者说操作系统)支持的内存上限

为arm内核构建源码树----Linux内核剖析(四)

前面说到要做linux底层开发或者编写Linux的驱动,必须建立内核源码树,之前我们提到过在本机上构建源码树--Linux内核剖析(三),其建立的源码树是针对i686平台的,但是我么嵌入式系统用的是arm平台,这就需要我们为arm板交叉构建一份板子可用的内核源码树. 首先下载与你嵌入式系统平台版本号一致的linux内核,我的版本为2.6.35,当然如果你使用的是之前做好的板子,那么内核源码可能已经有了 我们解压缩我们的内核源码,并进入到内核源码的根目录 tar -jxvf linux-2.6.3

Linux系统调用详解(实现机制分析)--linux内核剖析(六)

系统调用概述 计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同时运行的多个进程都需要访问这些资源,为了更好的管理这些资源进程是不允许直接操作的,所有对这些资源的访问都必须有操作系统控制.也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call).在linux中系统调用是用户空间访问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口. 一般情况下应用程序通过应用编程接口API,而不是直接通过系统调用来编程.在Unix世界,最流行的API

Linux内核剖析 之 回收页框

一.页框回收算法 1.为何要有页框回收算法?     Linux在为用户态与内核分配动态内存时,检查得并不严谨.     例如:     (1).对单个用户创建的进程的RAM使用的总量并不作严格的检查(进程资源的限制只针对单个进程):     (2).对内核使用的许多磁盘高速缓存和内存高速缓存大小也同样不做限制. 2.为何要减少控制?     可以使内核以最好的可行方式使用可用的RAM:     (1).当系统负载较低时,RAM的大部分由磁盘高速缓存占用,较少的正在运行的进程可以获益: