《深入理解Linux内核》读书笔记—第二章:内存寻址

硬件的分段单元

硬件的分段单元

从80386 CPU两种不同的地址转换模式:实模式(real mode)、保护模式(protected mode)

段寄存器(segmentation register)

逻辑地址:段标识符+段内相对地址的偏移量

段标识符是一个16位长的域,称为段选择符(segment selector),偏移量是一个32位长的域。

段寄存器:6个:cs,ss,ds,es,fs和gs。

同一个段寄存器用于不同的目的,方法是先将其值保存在内存中,用完后再恢复。【用完再恢复?用完都用完了恢复啥?主语不同吧?前一个用完再从内存恢复另一个】

cs:代码段寄存器,指向存放程序指令的段。
ss:栈段寄存器,指向存放当前程序栈的段。
ds:数据段寄存器,指向存放静态数据或者外部数据的段。
cs寄存器含有一个两位的域,用以指明CPU的当前特权级(Current Privilege Level,CPL)。
0:最高优先级,即:内核态
3:最低优先级,即:用户态

段描述符(segment descriptor)

大小:8个字节

存放位置:全局描述符表(Global Descriptor Table,GDT)或局部描述符表(Local Descriptor Table,LDT)

段描述符通常由编译器,链接器,加载器或者操作系统来创建,但绝不是应用程序。
GDT个数:1
LDT个数:每个进程1个
GDT在主存中的地址:放在gdtr寄存器中
正在被使用的LDT的地址:放在ldtr寄存器中
在这里插入图片描述
段描述符由以下域组成:

32位的Base域,含有段的第一个字节的线性地址。

粒度位G,如果被清0,段大小以字节为单位,否则单位以4096字节计。

在这里插入图片描述

【为什么BASE、LIMIT字段被拆的零零碎碎的?连续的表示不好吗?我猜是因为老的16位甚至8位系统的影响。可能是为了兼容。】

LIMIT(20bits):指定了段的长度,单位由粒度位G指定。G为0,段的长度范围为[1B, 1MB],G为1,段的长度范围为[4KB, 4GB]。4KB=4096B

系统标志S:S=0,系统段-存储内核数据结构;S=1,普通代码段或数据段。

Type域(4bits):描述了段的类型特征和它的存取权限。
代码段描述符

数据段描述符

任务状态段描述符(TSSD)

这个段描述符代表一个任务状态段(Task State Segment,TSS),也就是说这个段用于保存处理寄存器的内容(参见第三章中的“任务状态段”一节)。它只能放在GDT中。根据相应的进程是否正在CPU上运行,其Type的阈值分别为11(1011)或9(1001),S标志为0.

局部描述符表描述符

段选择符

在这里插入图片描述
段标识符也称为段选择符(Segment Selector),是一个12bit的字段。其组成为:
在这里插入图片描述
index(索引号):bit位3-15,指定相应段描述符的入口;

TI(Table Indicator)标志:bit位2,指明段描述符在GDT(TI=1)还是在LDT(TI=0);

RPL(请求者特权级):bit位0-1,当相应的段选择符装入到CS寄存器中时指示出CPU当前的特权级;

其中,段描述符在GDT和LDT中,TI标志所指明的GDT和LDT表示的是该段描述符是在全局描述符表(GDT)还是局部描述符表(LDT)。

段单元

在这里插入图片描述

内存地址

Inter80x86微处理器,区分以下三种不同地址

逻辑地址(logical address)【绪论中说也叫虚拟地址】

    机器语言指令仍用这种地址指定一个操作数或一条指令的地址。

    Intel有名的分段结构:把程序分成若干段,每个逻辑地址都由一个段(segment)和偏移量(offset)组成。

线性地址(linear address)

    是一个32位无符号整数,可以用来表示高达4GB的地址,线性地址通常用16进制数字表示。

物理地址(physical address)

    用于芯片级内存单元寻址。他们从微处理器的地址引脚发送到内存中线上的电信号相对应。物理地址由32位无符号整数表示。

图2-1 逻辑地址转换
图2-1 逻辑地址转换

Linux中的段

段可以给每个进程分配不同的线性地址空间

页可以把同样的先行地址空间映射到不同的物理空间

与分段相比,Linux更喜欢使用分页方式,因为:

当所有的进程使用同样的段寄存器值时,内存管理变得更简单,也就是说他们能共享同样的线性地址。

【是不是说两个进程:A和B,A的线程地址1=B的线性地址1,但是因为他们在不同的页上,所以实际上对应不同的物理地址?而如果是段的话,所有程序的线性地址空间不同】

段方式不是所有的处理机都支持。

Linux用到的段

内核代码段

——Base = 0x00000000

——Limit=0xfffff

——G(粒度标志)=1,段大小,单位以页计

——S(系统标志)=1,正常的代码段或数据段

——Type=0xa,可读并可执行的代码段

——DPL(描述符特权级)= 0,内核态

——D/B(32位地址标志)= 1,指偏移量为32位

相应的段选择符由__KERNEL_CS宏指定,为了在这个段内寻址,内核只需要把这个宏代表的值装进cs即可

内核数据段,在GDT中相应的段描述符的各个域有以下值(只写与代码段不同的):

——Type=2,可读写的数据段

实际上数据段与代码段的线性地址空间重叠。相应的段选择符由__KERNEL_DS宏定义

用户态下所有进程所共享的用户代码段,在GDT中相应的段描述符的各个域有以下值:

——Base = 0x00000000

——Limit=0xfffff

——G(粒度标志)=1,段大小,单位以页计

——S(系统标志)=1,正常的代码段或数据段

——Type=0xa,可读并可执行的代码段

——DPL(描述符特权级)= 3,用户态

——D/B(32未地址标志)= 1,指偏移量为32位

段选择符由__USER_CS宏定义。

用户态下由所有进程共享的用户数据段,在GDT中相应的段描述符的各个域有以下值:

——Type=2,可读写的数据段

段选择符由__USER_DS宏定义

任务状态段(TSS),每个进程有一个。这些段的描述符放在GDT中。与每个进程相关的TSS描述符的Base域包含相应进程描述符的tss域的地址。G标志被清0。由于TSS段是236字节长,限长域被设为0xeb。类型域设为9或11(可用的32位TSS),DPL设为0,因为用户态的进程不允许访问TSS段。【TSS域是什么?】

一个通常被所有进程共享的缺省LDT段。这个段保存在default_ldt变量中。这个缺省的LDT仅包含一个表项,即空端描述符。

硬件的分页单元

分页单元(paging unit)把线性地址转换成物理地址。它把所请求的存取类型和线性地址的存取权限相比,如果这次内存存取是无效的,则产生一个也错误异常。

页:为了效率起见,线性地址被分成以固定长度为单位的组,称为页。

页内连续的线性地址被映射到连续的物理地址中。用这种方式内核可以制定物理地址和页的存取权限,以代替它所包含的全部线性地址的存取权限。
在这里插入图片描述
分页单元认为所有的RAM被分成固定长度的页框(page frame)(有时叫物理页)。

每一个页框包含一页(page),也就是说一个页框的长度与一个页的长度一致。

页框是主存的一部分,因此也是一个存储区域。区分一页和一个页框是很重要的,前者只是一个数据块,可以被存放在任何页框或磁盘中。

把线性地址映射到物理地址的数据结构称为页表(page table)。页表存放在主存中,并在启用分页单元以前必须又内核对页表进行适当的初始化。

【程序开始执行时,根据运行参数在页表中加入新的页表数据?】

Intel处理器

CR0寄存器,通过设置PG标志启用分页。当PG=0时,线性地址就被解释成了物理地址。

常规分页

从i80386起,Intel处理器的分页单元处理4KB的页。32位的线性地址被分成3个域:

目录(Directory)

    最高的10位

页表(Table)

    中间的10位

偏移量(Offset)

    最低的12位

线性地址的转换 = 页目录表(page directory) + 页表(page table)

正在使用的页目录表的物理地址存放在处理器的CR3寄存器中。

页目录表最大:1024个(2的10次方)

页表最大:1024个

页最大:4096字节

寻址范围:1024 * 1024 * 4096=232
在这里插入图片描述
图2-5 Intel 80x86处理器的分页

【页目录包含所有内存也的目录吗?页目录和页表的对应关系是怎么设计的?】

页目录表项和页表项有同样的结构,每一表项都包含下面的域:

Present标志

    1:所指的页(或页表)就在主存中;

    0:不在主存中,此时这个表项剩余的位可由操作系统用于自己的目的(第十六章)

包含页框物理地址的最高20位的域(目录+页表)

    每个页框有4KB的容量,物理地址=N*4096,N为0或正整数。

Accessed标志

    存取标志:分页单元每次寻找相应页框地址时,都要设置存取标志。当所选中的页被交换出去时,这一标志就可能被操作系统使用。分页单元从来不重置这个标志,而是必须又操作系统去做。

    【?具体是什么情况?分页单元每次寻找响应页框也都是操作系统使用?硬件不会有该动作?】

Dirty标志

    只在页表项中使用。每次对一个页框进行写操作时,都要设置这个标志。和前面的情况一样,当操作系统选择一些页交换出去时会使用这个标志。分页单元从来不重置这个标志,而是必须由操作系统去做。

Read/write标志

    页或页表的存取权限。【目录呢?目录是只读的吧?】

User/Supervisor标志

    包含了访问页或页表所必须的特权级。PCD、PWT

Page Size标志

    只适用于页目录项。如果设置为1,页目录项指的是4MB的页框。【0:标准分页;1:启用扩展分页】

如果执行的地址转换所需的页表项或页目录项中的Present标志为0,分页单元就把这个线性地址存放在处理器CR2寄存器中,并产生14号异常,即“缺页”异常、。

【Present为0,代表不在主存中,那是在哪儿?交换到硬盘上了?】

扩展分页

扩展分页允许页框大小为4KB或4MB

目录域:最高10位(31~22)

偏移量:其余22位(1024*4096=4MB)
在这里插入图片描述
图2-6 扩展分页

扩展分页和正常分页的页目录表项基本相同,但除了:

Page Size标志必须被设置

20位物理地址域只有最高10位是有意义的。这是因为每一个物理地址都是在以4MB为边界的地方开始的,故这个地址的最为22位为0。

通过设置CR4寄存器的PSE标志能使扩展分页与常规分页共存。扩展分页用于把大段的连续线性地址转换成相应的物理地址,在这种情况下,内核可以不用中间页表而节省内存。

【大量的数据区在取其地址时,适合用扩展分页】

硬件保护方案
分段与分页的不同

分页举例

0x20000000~0x2003ffff
在这里插入图片描述
在这里插入图片描述
如果没有给这个进程分配其他线性地址,页目录的其余1023项都填为0。【每个进程都独立维护自己的页目录和页表?】

中间10位的值(即页表域的值)范围从00x03f,或十进制的从063.因而只有页表的前64个表项是有意义的,其余960表项都填为0.

假设进程需要读线性地址0x20021406中的字节。这个地址由分页单元按下面的方法处理:
在这里插入图片描述
如果页表第0x21表项的Present标志为0,此页就不在主存中。在这种情况下,分页单元在线地址转换的同时产生一个页异常。【交换到硬盘上去了?该异常会触发硬盘内容交换到内存?】

无论何时,当进程视图访问限定在0x20000000~0x2003ffff范围之外的线性地址时,都将产生一个页异常,因为这些页表项都填充了0,尤其是他们的Present标志都被清0。【所以一个进程中的内存异常不会影响其他进程。】

三级分页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值