16.内存使用与分段

【README】

1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;


【1】 内存使用

【1.1】程序加载到内存

1)内存使用:将程序放到内存中,PC寄存器指向开始地址;
2)把程序加载到内存;

【图解】

  • _entry入口程序的地址是0;
  • Call 40 表示调用偏移量为40的函数;

问题:

  • 上述代码是存储在磁盘上的;加载到内存后,call 40 如果还是调用偏移量为40的函数就是有问题的,因为操作系统程序在内存中从0地址开始,所以call 40调用会报错

解决方法:

  • call 40中的40是相对地址,需要修改为 call entry基址+40,如entry基址是1000,则修改为 call 1040 (以上修改程序中的内存地址的操作叫做重定位);

3)程序加载到内存的过程
需要找一段空闲内存,其基址为A,将磁盘上的程序加载到这段内存,执行重定位修改程序中的内存地址(重定位的具体做法是把载入的这段程序中的所有内存偏移量统统加上空闲内存的基址A)
然后程序取指执行,程序正常运行起来,内存也就被顺利使用了;

4)什么时候完成重定位?

  • 1.编译时:对于嵌入式系统可以在编译时完成重定位,但不灵活;
  • 2.载入时:程序加载到内存的时候,比较灵活(非嵌入式系统一般都采用载入时进行重定位); 如程序从磁盘载入到内存时,程序中的内存地址(地址偏移量)统统加上程序所在基址;

4.1)重定位优缺点:

  • 编译时重定位的程序只能放在内存固定位置(死板,编译时需要确定存放程序的空闲内存的基址);
  • 载入时重定位的程序一旦载入内存就不能动了(灵活,载入时才确定存放程序的空闲内存的基址);

 补充:

  • 补充1:40是逻辑内存地址,是内存相对地址,而1040是物理内存地址,是内存绝对地址;
  • 补充2:无论编译时重定位还是载入时重定位,一旦重定位操作后,程序指令操作的内存地址偏移量是不能够修改的;
  • 这又引入了新的问题: 很多时候程序载入内存后,还是会移动的;即业务场景是程序在运行过程中,其操作的内存地址偏移量需要变化的;如程序所在内存页的换入换出操作;

5)一个重要概念:交换swap

 初始态:

内存基址

内存载入的进程

磁盘存储的进程

2000

进程2

进程3

1000

进程1

第1次Swap后:

内存基址

内存载入的进程

磁盘存储的进程

2000

进程2

进程1

1000

进程3

第2次Swap后:

内存基址

内存载入的进程

磁盘存储的进程

2000

进程1

进程2

1000

进程3

 Swap的意思是:

  • 内存空间小,磁盘空间大。当内存装不下进程3时,会先把进程1换出到磁盘,再把磁盘上的进程3换入内存;
  • 简单说swap指的是进程在内存与磁盘间换入换出的操作;
  • 对进程1的多次换出换入操作,其内存地址(或基址)肯定会发生改变,这就造成程序指令操作的内地地址也会发生改变,所以在编译时或载入时进行重定位会导致swap后的进程执行错误;

所以应该是运行时重定位,而不是编译时或载入时进行重定位;


【1.2】运行时重定位  

1)运行时重定位定义:指的是 只有当执行指令的时候,才把指令的内存偏移量与基址相加得到操作数的内存地址;这样无论进行多少次swap,运行时重定位的内地地址都是正确的;

2)地址翻译(也叫重定位):

  • 运行时重定位,也叫地址翻译;每执行一条指令都要从逻辑地址算出物理地址;
  • 每次swap后,进程的内存基址都会修改,修改后的内存基址base存放在进程结构体PCB里面;即初始态下进程1的base等于1000,第2次swap后进程1的base等于2000;这样就能够正确计算指令中的内存地址偏移量对应的绝对物理地址;
  • 补充:当进程运行时,pcb中存储的进程基址base会送入基址寄存器存储,以便后续计算;

3)小结:程序如何使用内存?

  • 步骤1:在内存中找到一段空闲内存,并定位基址base(这段空闲内存的起始地址),并送入该进程pcb进行存储;
  • 步骤2:把程序放入步骤1申请的空闲内存中,以base为起始地址;
  • 步骤3:进程调度或上下文切换时,pcb里的base基址会送入基址寄存器存储;
  • 步骤4:每执行一条指令的时候,都进行地址翻译(重定位),即把内存地址偏移量加上基址base得到实际的物理内存地址,这个物理内存地址或者存放了程序指令或者是程序操作数;

经过以上步骤,程序就执行起来了;

【例】多进程执行时基地址切换

  • 步骤1:初始态, 进程1的基地址为2000,进程2的基地址为1000;

  • 步骤2:cpu执行进程1,进程1的pcb存储的基址2000送入基址寄存器;PC寄存器寻址到指令 mov ax,[100]并送入IR寄存器;
  • 步骤3:执行指令mov ax,[100]时,把100偏移量与基址2000相加得到操作数的物理内存地址(基址寻址);

  • 步骤4:cpu从进程1切换到进程2 (switch);
    • 切换后,进程2的pcb存储的基地址1000送入基址寄存器;这样后面执行的指令的操作数地址的基址都修改为1000了,达到进程切换后 逻辑地址能够正确翻译为物理地址的目的

 


【2】内存分段

1)一个程序由若干部分(段)组成,每个段有各自的特点,如主程序,变量集,函数库,动态数组,栈等;
2)每个部分或段中程序的内存地址偏移量都是相对于所在段的段基址的相对地址;
3)如何定位具体指令或数据: <段号, 段内偏移>

【例】mov [es:bx], ax
需要把以es为段基址,bx为偏移量的逻辑地址翻译为物理内存地址;

 4)程序分段对于存储的好处(分段存储采用的是分治思想

  • 好处1:不是将整个程序放入内存,而是将各段分别放入内存,提高内存利用率;
  • 好处2:在做swap时,不是把整个进程换入或换出,而是把进程的某个段换入或换出,提高了swap效率;且减少了swap次数;
  • 好处3: 程序段或代码段是只读的;(变量集)数据段是可写的;分段存储可以避免代码被误写的场景;

 
5)程序采用分段存储后,每个段都有自己的基址;
所以进程的pcb需要存储对应程序多个段的段基址;如进程段表所示:
                                 表1 进程段表

段号

基址

长度

保护

0

180K

150K

R

1

360K

60K

R/W

2

70K

110K

R/W

3

460K

40K

R

 补充:段0的偏移量30与段1的偏移量30翻译得到的物理内存地址是不一样,因为他们基址不一样;

6)GDT与LDT
可以把操作系统看做一个进程,其对应的段表叫做 GDT,结构同表1类似;
而每个进程也有自己的段表(用于存储程序多个段的基址),如表1所示,对应的结构体为LDT;

小结:在程序分段情况下使用内存的步骤;

  • 步骤1:把程序分为多个段,包括代码段,数据段等;
  • 步骤2:每个段在内存中找到一块空闲内存,并把这一段内存基址(起始地址)送入LDT表存储(如表1结构);LDT表就存储了该程序多个段的段基址;
  • 步骤3:把LDT表赋值给对应进程的PCB; 至此程序已经被载入到内存中了;
  • 最后:PC寄存器根据pcb设置初值,取指执行取指执行,在每执行一条指令的时候, 都查询LDT表找到段基址,并把该段基址加上地址偏移量得到物理内存地址,以进行后续的寻址操作;
  • 补充: ldt表基址送入ldtr寄存器;

【例】基于段基址的地址翻译
1)进程1的LDT表数据

段序号

段基址

1

1000

0

3000

2)进程2的LDT表数据

段序号

段基址

1

7000

0

5000

3)指令

  • 进程1的mov [cs:40], ax,其中cs代码段寄存器的值为0,即从ldt寻址下标为0的段基址(3000);所以cs:40得到的物理地址是3000+40=3040;
  • 进程2的mov [cs:40], ax,其中cs代码段寄存器的值为0,即从ldt寻址下标为0的段基址(5000);所以cs:40得到的物理地址是5000+40=5040;

因为当cpu从进程1切换到进程2时,首先pcb从pcb1切换到pcb2,所以pcb存储的ldt基址也会切换到ldt2并送入ldtr寄存器存储;
所以一旦ldtr被赋予新值,则段基址就会从进程1修改为进程2的段基址,达到多进程切换运行的目的;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1课 数组与内存控制 1.1 数组初始化 1.1.1 Java数组是静态的 1.1.2 数组一定要初始化吗 1.1.3 基本类型数组的初始化 1.1.4 引用类型数组的初始化 1.2 使用数组 1.2.1 数组元素就是变量 1.2.2 没有多维数组 1.3 小结 第2课 对象与内存控制 2.1 实例变量和类变量 2.1.1 实例变量和类变量的属性 2.1.2 实例变量的初始化时机 2.1.3 类变量的初始化时机 2.2 父类构造器 2.2.1 隐式调用和显式调用 2.2.2 访问子类对象的实例变量 2.2.3 调用被子类重写的方法 2.3 父子实例的内存控制 2.3.1 继承成员变量和继承方法的区别 2.3.2 内存中子类实例 2.3.3 父、子类的类变量 2.4 final修饰符 2.4.1 final修饰的变量 2.4.2 执行"宏替换"的变量 2.4.3 final方法不能被重写 2.4.4 内部类中的局部变量 2.5 小结 第3课 常见Java集合的实现细节 3.1 Set和Map 3.1.1 Set和Map的关系 3.1.2 HashMap和HashSet 3.1.3 TreeMap和TreeSet 3.2 Map和List 3.2.1 Map的values()方法 3.2.2 Map和List的关系 3.3 ArrayList和LinkedList 3.3.1 Vector和ArrayList的区别 3.3.2 ArrayList和LinkedList的实现差异 3.3.3 ArrayList和LinkedList的性能分析和适用场景 3.4 Iterator迭代器 迭代时删除指定元素 3.5 小结 第4课 Java的内存回收 4.1 Java引用的种类 4.1.1 对象在内存中状态 4.1.2 强引用 4.1.3 软引用 4.1.4 弱引用 4.1.5 虚引用 4.2 Java的内存泄漏 4.3 垃圾回收机制 4.3.1 垃圾回收的基本算法 4.3.2 堆内存的分代回收 4.3.3 与垃圾回收的附加选项 4.3.4 常见垃圾回收器 4.4 内存管理的小技巧 4.4.1 尽量使用直接量 4.4.2 使用StringBuilder和StringBuffer进行字符串连接 4.4.3 尽早释放无用对象的引用 4.4.4 尽量少用静态变量 4.4.5 避免在经常调用的方法、循环中创建Java对象 4.4.6 缓存经常使用的对象 4.4.7 尽量不要使用finalize方法 4.4.8 考虑使用SoftReference 4.5 小结 第5课 表达式中的陷阱 5.1 关于字符串的陷阱 5.1.1 JVM对字符串的处理 5.1.2 不可变的字符串 5.1.3 字符串比较 5.2 表达式类型的陷阱 5.2.1 表达式类型的自动提升 5.2.2 复合赋值运算符的陷阱 5.3 输入法导致的陷阱 5.4 注释的字符必须合法 5.5 转义字符的陷阱 5.5.1 慎用字符的Unicode转义形式 5.5.2 中止行注释的转义字符 5.6 泛型可能引起的错误 5.6.1 原始类型变量的赋值 5.6.2 原始类型带来的擦除 5.6.3 创建泛型数组的陷阱 5.7 正则表达式的陷阱 5.8 多线程的陷阱 5.8.1 不要调用run方法 5.8.2 静态的同步方法 5.8.3 静态初始化块启动新线程执行初始化 5.8.4 注意多线程执行环境 5.9 小结 第6课 流程控制的陷阱 6.1 switch语句陷阱 6.1.1 default分支永远会执行吗 6.1.2 break的重要性 6.1.3 switch表达式的类型 6.2 标签引起的陷阱 6.3 if语句的陷阱 6.3.1 else隐含的条件 6.3.2 小心空语句 6.4 循环体的花括号 6.4.1 什么时候可以省略花括号 6.4.2 省略花括号的危险 6.5 for循环的陷阱 6.5.1 分号惹的祸 6.5.2 小心循环计数器的值 6.5.3 浮点数作循环计数器 6.6 foreach循环的循环计数器 6.7 小结 第7课 面向对象的陷阱 7.1 instanceof运算符的陷阱 7.2 构造器的陷阱 7.2.1 构造器之前的void 7.2.2 构造器创建对象吗 7.2.3 无限递归的构造器 7.3 持有当前类的实例 7.4 到底调用哪个重载的方法 7.5 方法重写的陷阱 7.5.1 重写private方法 7.5.2 重写其他访问权限的方法 7.6 非静态内部类的陷阱 7.6.1 非静态内部类的构造器 7.6.2 非静态内部类不能拥有静态成员 7.6.3 非静态内部类的子类 7.7 static关键字 7.7.1 静态方法属于类 7.7.2 静态内部类的限制 7.8 native方法的陷阱 7.9 小结 第8课 异常捕捉的陷阱 8.1 正确关闭资源的方式 8.2 finally块的陷阱 8.2.1 finally的执行规则 8.2.2 finally块和方法返回值 8.3 catch块的用法 8.3.1 catch块的顺序 8.3.2 不要用catch代替流程控制 8.3.3 只能catch可能抛出的异常 8.3.4 做点实际的修复 8.4 继承得到的异常 8.5 小结 第9课 线性表 9.1 线性表概述 9.1.1 线性表的定义及逻辑结构 9.1.2 线性表的基本操作 9.2 顺序存储结构 9.3 链式存储结构 9.3.1 单链表上的基本运算 9.3.2 循环链表 9.3.3 双向链表 9.4 线性表的分析 9.4.1 线性表的实现分析 9.4.2 线性表的功能 9.5 小结 第10课 栈和队列 10.1 栈 10.1.1 栈的基本定义 10.1.2 栈的常用操作 10.1.3 栈的顺序存储结构及实现 10.1.4 栈的链式存储结构及实现 10.1.5 Java集合中的栈 10.2 队列 10.2.1 队列的基本定义 10.2.2 队列的常用操作 10.2.3 队列的顺序存储结构及实现 10.2.4 循环队列 10.2.5 队列的链式存储结构及实现 10.2.6 Java集合中的队列 10.3 双向队列 10.4 小结 第11课 树和二叉树 11.1 树的概述 11.1.1 树的定义和基本术语 11.1.2 树的基本操作 11.1.3 父节点表示法 11.1.4 子节点链表示法 11.2 二叉树 11.2.1 二叉树的定义和基本概念 11.2.2 二叉树的基本操作 11.2.3 二叉树的顺序存储 11.2.4 二叉树的二叉链表存储 11.2.5 二叉树的三叉链表存储 11.3 遍历二叉树 11.3.1 先序遍历 11.3.2 中序遍历 11.3.3 后序遍历 11.3.4 广度优先(按层)遍历 11.4 森林、树和二叉树的转换 11.4.1 森林、树和二叉树的转换 11.4.2 树的链表存储 11.5 哈夫曼树 11.5.1 哈夫曼树的定义和基本概念 11.5.2 创建哈夫曼树 11.5.3 哈夫曼编码 11.6 排序二叉树 11.7 红黑树 11.7.1 插入操作 11.7.2 删除操作 11.8 小结 第12课 常用的内部排序 12.1 排序的基本概念 12.1.1 排序概述 12.1.2 内部排序的分类 12.2 选择排序法 12.2.1 直接选择排序 12.2.2 堆排序 12.3 交换排序 12.3.1 冒泡排序 12.3.2 快速排序 12.4 插入排序 12.4.1 直接插入排序 12.4.2 折半插入排序 12.4.3 Shell排序 12.5 归并排序 12.6 桶式排序 12.7 基数排序 12.8 小结 第13课 程序开发 13.1 扎实的基本功 13.1.1 快速的输入能力 13.1.2 编程实现能力 13.1.3 快速排错 13.2 程序开发之前 13.2.1 分析软件的组件模型 13.2.2 建立软件的数据模型 13.3 弄清程序的具体实现 13.3.1 各组件如何通信 13.3.2 人机交互的实现 13.3.3 复杂算法的分析 13.4 编写开发文档 13.4.1 绘制建模图、流程图 13.4.2 提供简要说明 13.4.3 编写伪码实现 13.5 编码实现和开发心态 13.5.1 开发是复杂的 13.5.2 开发过程是漫长的 13.6 小结 第14课 程序调试 14.1 程序的可调试性 14.1.1 增加注释 14.1.2 使用log 14.2 程序调试的基本方法 14.2.1 借助编译器的代码审查 14.2.2 跟踪程序执行流程 14.2.3 断点调试 14.2.4 隔离调试 14.2.5 错误重现 14.3 记录常见错误 14.3.1 常见异常可能的错误原因 14.3.2 常见运行时异常可能的错误原因 14.4 程序调试的整体思路 14.4.1 分段调试 14.4.2 分模块调试 14.5 调试心态 14.5.1 谁都会出错 14.5.2 调试比写程序更费时 14.6 小结 第15课 使用IDE工具 15.1 何时开始利用IDE工具 15.2 IDE工具概述 15.2.1 IDE工具的基本功能 15.2.2 常见的Java IDE工具 15.3 项目管理 15.3.1 建立项目 15.3.2 自动编译 15.3.3 自动部署、运行 15.4 代码管理 15.4.1 向导式的代码生成 15.4.2 代码生成器 15.4.3 代码提示 15.4.4 自动代码补齐 15.4.5 实时错误提示 15.5 项目调试 15.5.1 设置断点 15.5.2 单步调试 15.5.3 步入、步出 15.6 团队协作功能 作为版本控制工具的客户端 15.7 小结 第16课 软件测试 16.1 软件测试概述 16.1.1 软件测试的概念和目的 16.1.2 软件测试的分类 16.1.3 开发活动和测试活动 16.1.4 常见的Bug管理工具 16.2 单元测试 16.2.1 单元测试概述 16.2.2 单元测试的逻辑覆盖 16.2.3 JUnit介绍 16.2.4 JUnit的用法 16.3 系统测试和自动化测试 16.3.1 系统测试概述 16.3.2 自动化测试 16.3.3 常见自动化测试工具 16.4 性能测试 16.4.1 性能测试概述 16.4.2 性能测试的相关概念 16.4.3 常见性能测试工具 16.5 小结
WinHex 是一款以通用的 16 进制编辑器为核心,专门用来对付计算机取证、数据恢复、低级数据处理、以及 IT 安全性、各种日常紧急情况的高级工具: 用来检查和修复各种文件、恢复删除文件、硬盘损坏、数码相机卡损坏造成的数据丢失等。得到 ZDNet Software Library 五星级最高评价,拥有强大的系统效用。功能包括 (依照授权类型): - 硬盘, 软盘, CD-ROM 和 DVD, ZIP, Smart Media, Compact Flash, 等磁盘编辑器... - 支持 FAT, NTFS, Ext2/3, ReiserFS, Reiser4, UFS, CDFS, UDF 文件系统 - 支持对磁盘阵列 RAID 系统和动态磁盘的重组、分析和数据恢复 - 多种数据恢复技术 - 可分析 RAW 格式原始数据镜像文件中的完整目录结构,支持分段保存的镜像文件 - 数据解释器, 已知 20 种数据类型 - 使用模板编辑数据结构 (例如: 修复分区表/引导扇区) - 连接和分割、以奇数偶数字节或字的方式合并、分解文件 - 分析和比较文件 - 搜索和替换功能尤其灵活 - 磁盘克隆 (可在 DOS 环境下使用 X-Ways Replica) - 驱动器镜像和备份 (可选压缩或分割成 650 MB 的档案) - 程序接口 (API) 和脚本 - 256 位 AES 加密, 校验和, CRC32, 哈希算法 (MD5, SHA-1, ...) - 数据擦除功能,可彻底清除存储介质中残留数据 - 可导入剪贴板所有格式数据, 包括 ASCII、16 进制数据 - 可进行 2 进制、16 进制 ASCII, Intel 16 进制, 和 Motorola S 转换 - 字符集: ANSI ASCII, IBM ASCII, EBCDIC, (Unicode) - 立即窗口切换、打印、生成随机数字 - 支持打开大于 4 GB 的文件,非常快速,容易使用。 - 广泛的联机帮助 [隐藏介绍]
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ...................................................................面试.................................................................................... 19 2.1. 线程 ...................................................................................................................................................... 20 2.2. JVM 内存区域 ..................................................................................................................................... 21 2.2.1. 程序计数器(线程私有) ................................................................................................................ 22 2.2.2. 虚拟机栈(线程私有) .................................................................................................................... 22 2.2.3. 本地方法区(线程私有) ................................................................................................................ 23 2.2.4. 堆(Heap-线程共享)-运行时数据区 ...................................................................................... 23 2.2.5. 方法区/永久代(线程共享) ..................................................................................................... 23 2.3. JVM 运行时内存 ................................................................................................................................. 24 2.3.1. 新生代 .......................................................................................................................................... 24 2.3.1.1. 2.3.1.2. 2.3.1.3. 2.3.1.4. Eden 区 .................................................................................................................................................... 24 ServivorFrom........................................................................................................................................... 24 ServivorTo ...........................................

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值