一.内存地址分类

对于程序员来说,可以简单的把内存地址理解为一种访问存储单元的内容的一种方式。而对于80x86系列微处理器来说,我们需要区分三种地址:

(1)逻辑地址

这种地址通常使用在机器语言里用于指定操作数或机器指令的地址。该类地址在著名的80x86分段体系架构中得到了很好体现,因此DOS程序员和windows程序员都不得不把他们的程序分成一段一段的(如代码段、数据段、堆栈段等等)。每一个逻辑地址都是由一个段(segment)和相对于段的实际起始地址的偏移地址(即offset 或 displacement)组成。

(2)线性地址(又称虚拟地址)

一个32位的无符号整数就可用于描述4GB(2的32次方=4G)的内存地址空间,相当于4,294,967,296个内存单元。线性地址通常用16进制表示,大小范围为:0x00000000 ~0xffffffff。

(3)物理地址

该类地址用于寻址/访问内存芯片里的存储单元。它们对应于微处理器引脚到内存总线之间的电信号。物理地址由一个32比特或38比特的无符号整数的16进制表示。

内存管理单元(MMU)可通过一个叫做段单元(segmentation unit)的硬件电路,将逻辑地址转换成线性地址;接着,通过一个叫做页单元(paging unit)的电路,再将线性地址转换成物理地址。如下图所示:

在有多个处理器的系统中,所有的CPU共享相同的内存。这就意味着,RAM芯片可能被相互独立的CPU们并发的访问。由于对RAM芯片的读操作和写操作都必须串行地执行,在总线和每个内存芯片之间添加了一个叫做内存仲裁器的硬件电路。这个电路的作用是:当RAM芯片处于空闲状态(这里的空闲,当然是指没有CPU在访问它)时,授权给CPU访问(俗称“放行”);当RAM芯片正忙(已经有CPU在使用该芯片)时,延迟(暂时禁止,即暂时“闭门谢客”)其他CPU对其访问。

甚至单处理器系统中,也会使用内存仲裁器。这是因为这些系统包括特殊的处理器------DMA控制器,它与CPU也存在并发操作的情况。当然,多处理器系统中的内存仲裁器电路更为复杂,因为它有更多的输入端口。例如,双核奔腾在每个芯片的入口维护了一个双端口的仲裁器,并且要求两个CPU在使用公用的总线时必须交换同步消息。从编程的角度看,仲裁器是隐而不见的,因为它是完全由硬件电路管理的。

二 逻辑地址到虚拟地址的转换

1.段选择符和分段寄存器

一个逻辑地址包括两部分:段标识符和 段内相对偏移地址。段标识符是一个被叫做段选择符(selector)的16比特的域,而偏移地址是一个32比特的域。

为了方便快速检索段选择符,处理器提供了6个分段寄存器(segmentation register)来缓存段选择符,它们是:cs,ss,ds,es,fs和gs. 虽然只有这6个寄存器,但程序可以复用同一个寄存器来实现不同的目的,只需要把该寄存器的内容保存到内存中,在随后需要的时候可以恢复它的内容。需要注意的是,cs、ss、ds有专门的用途。请看下面介绍:

cs-----内存段寄存器,指向含有代码指令的段;cs寄存器还有一个重要的功能:用于区分用户模式和内核模式,它包含一个指定当前优先级别(CPL, current priviledge level)的的2比特的域。如果该域的值为0,表明优先级最高;如果该值为3,表明最低的优先级。 Linux只使用了0和3,用以区分内核模式和用户模式。

ss-----堆栈段寄存器,指向包含当前程序栈的段;

ds-----数据段寄存器,指向包含静态和全局数据的段。

其它三个,即es,fs和gs,都是通用分段寄存器,可以指向任意类型的段。

2.段描述符

每一个段都由一个8字节的段描述符来表示,它描述了段的特征。段描述符要么存储在全局描述符表(GDT)里,要么存储在本地描述符表(LDT)里通常只定义了一个GDT。然而,每个进程都允许有自己的LDT,如果进程需要额外创建除了GDT里描述的之外的段。主存里GDT的地址和大小都包含在gdtr控制寄存器中,而当前正在使用的LDT的地址和大小则包含在ldtr控制寄存器中。

BASE: 段的第一个字节的线性地址。

G: 如果为0,则段的大小用字节表示。

Limit: 保存了段中最后一个存储单元的偏移值,因此与segment的长度、大小是绑定在一起的。如果G为0,则LIMIT的大小范围为1字节~1MB;反之, LIMIT大小范围为4KB~4GB.

S: 如果为0,表示为一个系统段(system segment);反之,为一个普通的数据段或代码段。系统段里保存了关键的数据结构,如LDT.

Type:描述segment的类型。

DPL: 描述符优先级别。主要用于对segment的访问进行限制。

P: 描述段是否在内存中存在的标记。

3.对段描述符的快速访问的实现

我们知道,逻辑地址由一个16比特的段选择符和一个32比特的偏移地址组成。同时,我们也知道,分段寄存器里只存储了段选择符。我们先接着第一节继续对段选择符进行分析。它的格式如下所示:

Index:标记了GDT或LDT中段描述符的入口。由于段寄存器有8个字节长,它在GDT或LDT中的相对地址是这样来计算的:13个bit之长(如上图,比特3-15位)的index域值乘以8. 假设GDT位于0x00020000 (该值存放在gdtr控制寄存器中) 并且 段选择符的index域值为2,那么相应的段描述符的地址是这么来计算的:

0x00020000 +(2 x8),即0x00020010.

TI: table indicator。TI=0,表示段描述符位于GDT中;TI=1,表示段描述符位于LDT中。

RPL:Requestor Previlige Level请求者优先级。

为了加快逻辑地址到线性地址的转换过程,80x86增加了一个不可编程的寄存器。

每当一个段选择符被加载到分段寄存器中时,相应的段描述符也被从内存里加载到那个匹配的不可编程的CPU寄存器中。这样,逻辑地址的转换就不再需要访问主内存中的GDT和LDT,而只需要访问那个包含段描述符的不可编程的寄存器。只有在分段寄存器内容改变时,才需要访问LDT或GDT。

3.分段单元

我们知道,内存管理单元(MMU)可通过一个叫做分段单元(segmentation unit)的硬件电路,将逻辑地址转换成线性地址;接着,通过一个叫做分页单元(paging unit)的电路,再将线性地址转换成物理地址。如下图所示:

那么,这个分段单元是按照什么样的流程完成自己的职责所在呢?

首先,它会检查段选择符的TI域,进而知道是哪个描述表存放了相应的段描述符。如果段描述符位于GDT,则分段单元从gdtr寄存器中读取GDT的线性基地址;否则,分段单元从ldtr读取LDT的线性基地址。

其次,根据上一步得到的线性基地址和段选择符的index域,计算出段描述符的地址。计算方法可参考上节。

通过以上两步,我们就可以定位到我们需要的段描述符。

最后,把逻辑地址的偏移与前面定位到的段描述符的线性地址BASE域相加,得到线性地址。这样,整个逻辑地址到线性地址的转换过程就成了

地址转换的过程如下图所示:

注意,本文中的线性地址跟虚拟地址是一个概念。

3 Linux的分段机制

我们知道,内核不能寻址超过1G大小的RAM空间,因此当RAM的空间大于1G的时候,就会被引入一个比较模糊的概念---分段。80x86体系的处理器中,它们鼓励程序员把程序化分成逻辑上相关的实体,例如子程序或者全局与局部数据区。但是我们的Linux并不是完全地使用这个机制,它只是以极为有限的方式引入这种方式。分段可以把每一个进程分配不同的线性地址空间,而分页则可以把相同的线性地址空间映射到不同的物理空间。在Linux的2.6版本中,进行在内核态的所有Linux进程都使用一对相同的段对指令和数据寻址:它们分别叫做内核代码段和用户数据段。

注意:与段相关的线性地址从0开始,这可以达到2^32 -1的寻址限长。也就是说在用户态或者内核态下的所有进程可以使用相同的逻辑地址。Linux下逻辑地址和线性地址都是一样的。

linux分段加载程序_Linux的分段机制相关推荐

  1. linux待机唤醒_Linux睡眠唤醒机制--Kernel态

    一.对于休眠(suspend)的简单介绍 在Linux中,休眠主要分三个主要的步骤: 1) 冻结用户态进程和内核态任务 2) 调用注册的设备的suspend的回调函数, 顺序是按照注册顺序 3) 休眠 ...

  2. 深入理解计算机系统-之-内存寻址(四)--linux中分段机制的实现方式

    linux中的分段机制 前面说了那么多关于分段机制的实现,其实,Linux以非常有限的方式使用分段.因为,Linux基本不使用分段的机制(注:并不是不使用,使用分段方式还是必须的,会简化程序的编写和运 ...

  3. linux 内存 段,Linux内存储器管理之分段机制

    Linux内存管理之分段机制 逻辑地址就是我们普通的段+偏移的表现方式,而线性地址就是段+偏移之后算出来的一个地址,前者可以认 为是二维的地址,而后者可以理解是一维的.线性地址和虚拟地址的概念相接近, ...

  4. Linux内存管理:内存寻址之分段机制与分页机制

    目录 Linux 内存寻址之分段机制 前言 分段到底是怎么回事? 实模式的诞生(16位处理器及寻址) 保护模式的诞生(32位处理器及寻址) IA32的内存寻址机制 寻址硬件 IA32的三种地址 MMU ...

  5. Linux内存管理之内存寻址:分段机制的实现方式

    Table of Contents linux中的分段机制 linux中的GDT 用户态和内核态的数据段以及代码段4个段 任务状态段TSS 寄存器保存区域 内层堆栈指针区域 地址映射寄存器区域 链接字 ...

  6. Linux下程序的保护机制(checksec)

    Linux下程序的保护机制 前言 相信很多人,查看程序信息时会用到,checksec这个命令.它会给你返回如下图的结果,但是很多最开始看到的人,很多都看不懂,如果身为小白的我,跟在大佬后面比葫芦画瓢, ...

  7. Linux并发程序课程设计报告,网络操作系统课程设计--进程机制与并发程序设计-linux下生产者与消费者的问题实现.doc...

    网 络 操 作 系 统 课 程 设 计 网络操作系统课程设计 设计内容:进程机制与并发程序设计inux下生产者与消费者的问题实现进程机制与并发程序设计inux下生产者与消费者的问题实现 (1)掌握基本 ...

  8. linux多用户运行同一程序_linux系统中CentOS有哪些优势,让它长盛不衰?

    CentOS是目前评价和口碑都不错的linux系统,甚至很多公司安装的都是CentOS系统,对于初入门的小白可能不了解这个系统,今天我们就整理一下CentOS系统的七大优势. 1. 开源.免费 众所周 ...

  9. 试驾小程序_Linux如何成为Linux:试驾1993-2003发行版

    试驾小程序 开源的独特之处在于它永远不会真正终止生命(EOL). 光盘映像大部分保持在线状态,并且其许可证不会过期,因此返回并在虚拟机中安装旧版本的Linux并准确了解Linux多年来取得的进展是相对 ...

  10. linux 各用户内存_Linux用户空间与内核空间(理解高端内存)

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

最新文章

  1. LeetCode简单题之检查是否所有字符出现次数相同
  2. 当定时任务遇上随机数
  3. A*算法解决八数码问题 Java语言实现
  4. Leaflet快速入门与加载OSM显示地图
  5. Java中创建对象的四种方式
  6. [z]如何在一台windows主机上安装多个mysql服务
  7. Live Migrate 操作 - 每天5分钟玩转 OpenStack(42)
  8. 在大流行的世界中如何建立技术社区
  9. 浅谈高斯消元的实现和简单应用
  10. 实战系列-被面试官问到Feign原理
  11. (c语言)将一个数组逆序输出
  12. 机器学习算法与Python学习
  13. slub释放过程-do_slab_free
  14. OpenCV + VS + Python
  15. 我的电子产品开发资料学习资料免费下载地址(超值超值。。。)
  16. 短视频推广引流方案怎么做?看这五点,让你快速裂变涨粉
  17. java判断日期是当天_Java判断日期为昨天 今天 明天
  18. linux防火墙更改端口号,Linux防火墙开放某端口号
  19. Hadoop集群启动时,nameNode进程没有启动 :Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password)解决方法
  20. 我现在也晕菜了(一)

热门文章

  1. Memory stream is not expandable
  2. php 微信公号授权登入,laravel实现微信公众号授权登录实战
  3. SXLib3D -- 一款高效的点云和网格交互处理平台
  4. 技术文档系列之架构设计文档模板
  5. 网站搭建教程(详细步骤 )
  6. mysql源代码_MySQL源代码解读(一)
  7. 深入Marlin固件
  8. ubuntu1804安装python3.8+odoo14
  9. 万卷书计划-2016年开启
  10. 通过doi可以检索到文献_什么是DOI?如何获取文献的DOI?