操作系统 3. 内存管理 2021-04-07 浏览量 616 暂无评论 [TOC] ## 3.1 内存管理 ### 3.1.1 内存使用与分段 时间:2021-03-28 场景:汇编中,call 40这条指令表明要跳到40这个地址(逻辑地址、相对地址)执行,那么把程序载入到内存中时,物理内存的40位置一定要存放对应的程序,如果不对内存进行分段的话,这显然不切实际,因为40这个地址可能存放了别的程序(事实上,0开始的位置存放的是操作系统),所以,程序在载入内存时应当找到一段空闲内存,并修改相应的逻辑地址为物理地址(==重定位==)。 重定位也有两种方式: 1. 编译时(硬系统,如一些嵌入式的系统,效率高,但不灵活) 2. 载入时(灵活通过,==但目前来讲,程序载入进内存就不能移动了==) 怎么才能让载入内存的程序实现移动呢? ==swap==:如果进程发生阻塞,那把它放在内存中有点浪费内存,这时候将该进程睡眠,换出到磁盘(已经重定位过了物理地址),想执行的时候再换入,那么问题来了,怎么保证它换入时的物理内存地址和原来一样呢?==运行时重定位(地址翻译)== `base+offset` `base`保存在哪呢?==PCB====>==基址寄存器== 还有个问题:如果找不到一段足够大的空闲内存放程序怎么办呢? 将程序分段,每个段有各自的特点、用途,如代码段(只读)、数据段(可写),每个段都是从0开始编址(用户可独立考虑每个段[==分治==]) 这样就可以将一个程序的每个段分别放入内存,==段号+段内偏移== 这时候PCB里要存放每个段的基址(进程段表LDT,和操作系统对应的段表GDT同理) ### 3.1.2 内存分区与分页(Memory Partition and Paging) 时间:2021-03-29 **程序进入内存的步骤:** 1. 程序分段 2. 在内存中找出空闲区域 3. 将程序从磁盘中读到内存 **为什么要分区?** 由上一小节很容易引出内存分区,因为要看哪些部分是空闲的,大小为多少 **怎么分区呢?** 固定分区与可变分区 可变分区的管理:请求分配,释放内存,再次申请(涉及到分配哪块空闲的) 分配哪块呢?首选适配(第一个,快),最佳适配(大小最合适,O(n)),最差适配(每个段均匀) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210329221126223.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzkyOTU5,size_16,color_FFFFFF,t_70#pic_center) 实际的系统的物理内存并不使用内存分区,而是内存分页 **为什么要分页?** 解决内存分区导致的内存效率问题,比如内存碎片,需要将空闲分区合并(内存紧缩),但执行该操作的时候CPU不能干别的。 **怎么分页?** 将内存分成,比如每4K一页,针对每个段内存请求,将段打散,系统一页一页地分配给这个段,(那重定位就变得复杂点,得知道它想跳转的地方被分到了哪个页,地址//页大小(根据这个找到页框的物理地址)+地址%页大小),需要用到页表(cr3),==MMU会自动计算== ### 3.1.3 多级页表和快表 时间:2021-04-01 之前的分页方法有什么问题? 为了提高内存的空间利用率,页应该小,但是页小了页表就大了 怎么解决? 尝试1:分配给进程中的逻辑页中,有的逻辑页用不到,(要问为什么用不到还要分配给它的话,考虑重定位时的逻辑页总数:地址//页大小 + 1),那就只保留用到的逻辑页的页表项 问题1:这时的页表不连续,查找起来费时,顺序查找-->折半查找,但这也费时,降低运行速度,页号连续的话查找就很快,怎么结合这两个呢?==多级页表就来了== 尝试2:页目录表+页表(类比书的目录) 可以,但访问内存的次数变多,时间上更奢侈 尝试3:快表(TLB,相联快速存储,是寄存器) 快表中存放最近使用的页表项,找不到的话再到多级页表中找 要让快表好使,它的命中率一定要高,所以快表越大越好(但它贵呀,一般64~1024);同时,置换的算法也很重要 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021040121290171.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzkyOTU5,size_16,color_FFFFFF,t_70) ### 3.1.4 段页结合的实际内存管理 程序(分段)-->虚拟内存(地址空间,段->页)-->物理内存(分页) 逻辑地址-->虚拟地址-->物理地址 段面向用户,页面向硬件 虚拟内存不用管理吗?还是说浪费了也没事,有碎片就有碎片,反正不贵 ``` 段页式内存下程序如何载入内存? 1. 在虚拟内存中分配段 2. 将程序对应段放到虚拟内存对应段; 3. 将虚拟内存的段分页放到物理内存; 4. 建立页表; 5. 使用。 ``` ## 3.2 虚拟内存 使用换入换出实现虚拟内存。 ### 3.2.1 请求调页与内存换入(Swap in) 2021-04-06 物理内存小于虚拟内存,虚拟内存比较规整,就算物理内存没那么大,虚拟内存也能让用户感觉可以使用那么大,不用因为物理内存的大小而头疼,那么就**使用换入换出实现“大内存”**,用到程序的哪段就把这段从硬盘换入到物理内存。 ### 3.2.2 内存换出(Swap out) 2021-04-07 还记得请求调页时候的`get_free_page`吗?而内存是有限的,并不能总是获得新的页,需要选择一页淘汰,换出到磁盘,那怎么选择呢?用缺页次数来评价方案的好坏。 - FIFO,刚换出去又要换进来的话,就很麻烦 - MIN,选最远将使用的页淘汰,是最优方案,但是要知道将来发生的事 - LRU(Least recently used),用过去的历史预测将来,选最近最长一段时间没有使用的页淘汰 LRU怎么实现呢? **时间戳(time stamp)** 每页维护一个时间戳,记录该页在第几个时刻使用到了。(每执行一条指令,都要维护这个时间戳) **页码栈** 每执行一条指令,都要更新栈 LRU近似实现SCR[又叫Clock Algorithm](因为准确实现太奢侈) 将时间计数变为是和否,每页加1个引用位R,每次访问一页时,硬件自动设置该位(就不用软件维护时间戳了,直接MMU),选择淘汰页:扫描该位,是1时清0,并继续扫描,是0时淘汰。 但如果缺页很少的话,几乎所有的R都为1(因为只有缺页时才会将0变为1),那需要换页的时候,指针会循环一遍,把所有的1变为0,然后换掉第一次指向的页,SCR退化为FIFO。 原因:记录了太长的历史信息 怎么办:定时清除R位(再来一个快的扫描指针),确保“最近” 给进程分配多少页框(frame)呢? 求工作集,覆盖局部。 与页框分配有关的问题:系统颠簸 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210407223739931.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzkyOTU5,size_16,color_FFFFFF,t_70) 赞赏 微信支付 支付宝支付