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

简介:

1内存地址

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

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

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

内存条硬件图:

 

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

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

 内存条抽象图:


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


 


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

2.1 段选择符与段寄存器

逻辑地址:段选择符(Segment Selector16位)+段内偏移(Offset32位)


Index:在GDTLDT中段描述符的位置

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: 段基地址

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

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

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

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

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


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

代码段描述符:

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

数据段描述符:

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

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

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

局部描述符表描述符(LDTD

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

结构:

 

 


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


具体操作:

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

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

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


3Linux中的分段

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 StorageTLS):线程私有数据,系统调用set_thread_area()get_thread_area()分别为正在执行的进程创建和撤销一个TLS段。

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

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

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

        #G标志清0Limit0xeb也就是段长为236字节Type字段置为911DPL0,不允许用户态访问。

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

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

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

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

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

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

目录
相关文章
|
17天前
|
缓存 NoSQL Linux
Linux系统内存使用优化技巧
交换空间(Swap)的优化 禁用 Swap sudo swapoff -a 作用:这个命令会禁用系统中所有的 Swap 空间。swapoff 命令用于关闭 Swap 空间,-a 参数表示关闭 /etc/fstab 文件中配置的所有 Swap 空间。 使用场景:在高性能应用场景下,比如数据库服务器或高性能计算服务器,禁用 Swap 可以减少磁盘 I/O,提高系统性能。
34 3
|
17天前
|
缓存 Linux
Linux查看内存命令
1. free free命令是最常用的查看内存使用情况的命令。它显示系统的总内存、已使用内存、空闲内存和交换内存的总量。 free -h • -h 选项:以易读的格式(如GB、MB)显示内存大小。 输出示例: total used free shared buff/cache available Mem: 15Gi 4.7Gi 4.1Gi 288Mi 6.6Gi 9.9Gi Swap: 2.0Gi 0B 2.0Gi • to
29 2
|
1月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
125 48
|
2月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
|
2月前
|
缓存 Linux
linux 手动释放内存
在 Linux 系统中,内存管理通常自动处理,但业务繁忙时缓存占用过多可能导致内存不足,影响性能。此时可在业务闲时手动释放内存。
136 17
|
2月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
185 20
|
2月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
137 15
|
3月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
1天前
|
消息中间件 Java 应用服务中间件
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
JVM实战—2.JVM内存设置与对象分配流转
|
3天前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略