Linux运行的硬件基础
Linux运行的硬件基础¶
i386的寄存器¶
i386 -> 80386
-
通用寄存器
-
EAX
: add EBX
: baseECX
: countEDX
: dataESP
: stack pointerEBP
:base pointerESI
: source indexEDI
: destination index
通常存32位数据
- 与16位兼容:低位->8个16位寄存器。
AX\BX\...\DI
- 与8位兼容:进一步将
EAX\EBX\ECX\EDX
低位16位,分为8位高位字节和8位低位字节,作为8个8位寄存器(AH、BH、CH、DH 和AL、BL、CL、DL
)high
low
-
对8位和16位寄存器的操作只影响相应的寄存器
-
段寄存器
-
CS
: code DS
: dataSS
: stack-
ES
\FS
\GS
: 附加 -
状态和控制寄存器
-
EFLAGS
: 标志寄存器TF
:自陷标志- 1:单步执行,执行完成可能产生异常1的自陷(每执行完一条指令都由异常1处理程序(debug())进行检验)
- 0:且将断点地址装入调试寄存器
DR0~DR3
时,才会产生异常1 的自陷 IOPL
: 输入输出特权等级,保护模式下- 两位:取值1、2、3、4(级别)
- Linux内核:0(内核级)3(用户级)
CPL(当前特权级)
> IOPL: IN、OUT、INS、OUTS、STI、CLI 和LOCK 等指令而不会产生异常13(即 保护异常)CPL == 0
:POPF(pop from stack top flags)
和中断返回指令可以改变IOPL的值IF(interrupt flag)
- 1:允许CPU接收外部中断请求
- 0:禁止外部中断
- 在保护模式下,只有当第12、13 位指出当前CPL 为最高特权级时,才允许将新值置入标志寄存器(EFLAGS)以改变IF 位的值
DF
: 定向标志ESI
orEDI
增值或者减值- 1:减
- 0:增
NT
:嵌套任务标志位- 保护模式下使用
- 发生中断或者执行CALL,可能引起任务切换(NT:1 else NT:0)
VM
:虚拟8086方式标志- 1:保护模式,使全部段操作就像是在8086 CPU上运行一样
-
EIP
: 指令指针寄存器,存放下一条将要执行指令的offset,与目前正在运行的CS存放的基地址->下一条指令的地址(低16位可以分开来访问,指令指针IP寄存器,用于16位寻址) -
4个控制寄存器:CR0、CR1、CR2、CR3
- CR3 是页目录基址寄存器,保存页目录表的物理地址。页目录表总是放在以 4KB 为单位 的存储器边界上,因此,它的地址的低 12 位总为 0,不起作用
-
系统地址寄存器
保存操作系统要保护的信息和地址转换表信息
- 任务状态寄存器:16位,保存TSS段的16位选择符
- 调试和测试寄存器
内存地址¶
- 逻辑地址:段 + 偏移量
- 线性地址(虚拟地址):32位
unsigned int
,4GB,取值范围0x00000000~0xffffffff
- 物理地址:内存单元的实际地址,32位
unsigned int
段机制和描述符
描述符:描述段的属性,8字节的存储单元
-
实模式下:代码段、数据段、堆栈段、段的起始地址、段的长度
-
保护模式:复杂一些
-
(用户)段描述符:32位基地址和20位段界限
-
G:粒度位
- 0:一个段最长可达1M字节
- 1:一个段最长可达1M乘以4K = 4G字节
-
D:缺省操作数的大小
- 0:操作数为:16
- 1:操作数为:32
-
第6个字节其余两位为0,为了与将来的处理器兼容
-
第5个字节:存取权字节
-
P:存在位(Present),表示段描述符描述的这个段是否存在内存中
-
1:在
-
0:不在
-
DPL:描述符特权等级,确定保护等级
-
S:system,表示这个段是系统段还是用户段
-
0:系统段
-
1:用户程序的代码段、数据段、堆栈段
-
类型:
- 第3位:E(execute)表示是否可执行
- 0:数据段描述符
- 第2位ED:扩展方向
- 0:向地址增大的方向扩展,存取数据段中的数据的偏移量必须小于或等于段界限
- 1:向地址减小的方向扩展,偏移量必须大于界限
- 第1位W:可写
- 0:不能写
- 1:可写入
- 在80386当中,堆栈段也被看成特殊的数据段
- 1:代码段描述符
- 第2位C:一致位
- 1:如果当前特权级(CPL)低于描述符特权级,并且当前特权级保持不变,那么代码段只能执行
- 第1位R:可读
- 0:不能读
- 1:可读
- 第0位:A访问位,用于请求分段不分页的系统中,每当该段被访问时,将A 置1。对于分页系统,则A 被忽略未用
-
-
系统段描述符
- 系统段描述符的第5 个字节的第4 位为0,说明它是系统段描述符,类型占4 位,没有A 位。第6 个字节的第6 位为0,说明系统段的长度是字节粒度,所以,一个系统段的最大长度为1M 字节
- 类型(16种)
-
描述符表
-
各种各样的用户描述符和系统描述符,都放在对应的全局描述符表、局部描述符表和中断描述符表中
- 8的倍数的存储器空间:空间大小在8 个字节(至少含一个描述符)到64K 字节(至多含8K)个描述符之间
- GDT:除了任务门,中断门和陷阱门描述符外,包含着系统中所有任务都共用的那些段的描述符。它的第一个8 字节位置没有使用
- IDT:包含256 个门描述符,能包含任务门、中断门和陷阱门描述符,只能存取2K字节以内的描述符,即256 个描述符,这个数字是为了和8086 保持兼容
-
LDT:每个任务各自有一个的LDT,可以使给定任务的代码、数据与别的任务相隔离
- 每一个任务的局部描述符表LDT 本身也用一个描述符来表示,称为LDT 描述符,它包含了有关局部描述符表的信息,被放在全局描述符表GDT 中
-
选择符与描述符表寄存器
-
实模式下:段寄存器存放的是真实的段地址;保护模式下:段寄存器(16位无法存32位的段地址)存放的是段选择符
-
选择符
- TI:1的时候,从局部描述符表中取描述符;0的时候,全局描述符表
- RPL(requestor):请求者特权级,表示描述符的特权级,只有请求者特权级RPL 高于(数字低于)或等于相应的描述符特权级DPL,描述符才能被存取,实现一定程度的保护
-
段描述符高速缓冲寄存器
当选择符的值改变时,处理器自动装载不可见部分
-
寻址过程
-
描述符投影寄存器
-
Linux的段
-
从2.2 版开始,Linux 让所有的进程(或叫任务)都使用相同的逻辑地址空间,因此就没有必要使用局部描述符表LDT。但内核中也用到LDT,那只是在VM86 模式中运行Wine 时,即在Linux 上模拟运行Windows 软件或DOS 软件的程序时才使用
- TI=0,并把这4 个段都放在GDT中, index 就是某个段在GDT 表中的下标
- 内核代码段和数据段具有最高特权,因此其RPL为0,而用户代码段和数据段具有最低特权,因此其RPL 为3
- GDT 放在数组变量gdt_table 中
- 每个段的逻辑地址空间范围为0~4GB,每个段的基地址为0,偏移量就是线性地址,我们以后所提到的逻辑地址(或虚拟地址)和线性地址指的也就是同一地址
- 把段分为两种:用户态(RPL=3)的段和内核态(RPL=0)的段
- 描述符投影寄存器的内容很少发生变化,只在进程从用户态切换到内核态或者反之时才发生变化
- 用户段和内核段的区别在其RPL 不同,因此内核根本无需访问描述符投影寄存器,当然也无需访问GDT,而仅从段寄存器的最低两位就可以获取RPL 的信息
- Linux 的进程没有使用LDT,每个CPU 仅使用一个TSS(任务状态段)
分页机制¶
-
线性 -> 物理地址
-
由CR0中的PG位启用
-
1:开启
- 0:禁止分页机制
-
每一页4K大小,4K对齐 --> 线性地址的低12 位经过分页机制直接地作为物理地址的低12 位使用
-
页被标记为无效
-
是线性地址是操作系统不支持的地址:程序因产生了无效地址而必须被终止
- 在虚拟存储器系统中,线性地址对应的页存储在磁盘上,而不是存储在物理存储器中:该无效的地址实际上是请求操作系统的虚拟存储管理系统,把存放在磁盘上的页传送到物理存储器中,使该页能被程序所访问
无效页通常是与虚拟存储系统相联系,这样的无效页通常被称为未驻留页:未驻留页是程序可访问的页,但它不在主存储器中。对这样的页进行访问,形式上是发生异常,实际上是通过异常进行缺页处理
-
分页机构
-
两级页表结构:对线性地址高20位的线性-物理地址转换分为两部分完成,每一步使用其中的10位
-
页目录:页目录项
-
页面项
-
地址转换
-
扩展分页
-
TLB
- Linux中的分页机制