简述操作系统内存管理

        这篇可是真枯涩啊,哈哈,老早在学操作系统的时候整理的文章,没加润色,单纯从个人网站迁移过来。

        操作系统内存管理的目的是将线性物理地址用抽象的逻辑地址空间,从而保护物理地址。此外,可以独立地址空间,共享内存以及虚拟化。

        操作系统内存管理方式:

        1、重定位;

        2、分段;

        3、分页;

        4、虚拟存储;

重定位

        重定位的作用就是将程序地址重定位到另一个地址。最早在连续内存管理中使用通常来讲,连续内存分配我们地址空间生成时主要有几个节点:

        1)编译 ,绝对装入

        假如起始地址已知,会根据相对地址,在编译时生成绝对地址。比如最早的功能手机,都是这样的,是不允许自安装什么软件的。

        2、加载

        在编译时如果起始位置未知,就进行重定位。加载时,生成绝对地址。也称静态重定位

        3、执行时

        执行时代码可移动,实现重定位,也称为动态重定位。

        内存分配主要有连续和非连续内存分配两种方式。

        连续是最早的内存分配方式,主要是要为程序找到一片连续的内存空间,这要求还是很高的。此外不同的连续内存分配还会出现内存利用率低,容易产生碎片等缺点。

        因为,目前都是采用非连续内存分配。

基于此,非连续内存分配目标:

        1、提高系统内存利用率;

        2、共享某些内存;

        3、支持动态加载和动态链接;

        选择非连续内存块大小方法主要有段式和页式两种方法。

段式存储管理

        将进程地址空间划分成多个段,比如数据段,代码段,堆栈等等。

        段和段的地址可以是不连续的,但段内的是一块连续空间。每个段的地址都是从0开始,并有偏移地址。逻辑地址由二元组 (s,addr) 表示。其中 s 表示段号,addr 表示段内偏移量。

        整个进程地址空间是个二维数组。

        此外,系统会为每个进程维护一个段表.当我们在执行程序时,会首先根据段号到进程段表中找到段基址和长度,然后MMU会判断是否越界,如果没问题,会根据基址和偏移得到实际物理地址。

        段式存储可以实现信息的保护和共享。

        MMU会对检查段表项的保护位,如果越界就会报异常。此外,使用可重入代码时,只需在段表中为其设置一个段表项,并将执行时用到的部分数据copy到局部数据区,可以实现信息共享。

页式存储管理

        页帧Frame:物理地址分配的基本单元;

        页面Page:逻辑地址分配的基本单元;

        要完成页面到页帧的转换需要通过页表,页表保存了逻辑地址和物理地址的映射关系。页表的机制会存储在页表基址寄存器中,它会随着进程的运行不断地变化。

        页分配相比于段,可以让进程地址空间更不需要连续空间,不会产生碎片化。但其一个问题是,由于页比较小,可能使得页表变得特别大,使得访问性能变差。不过我们可以通过缓存(块表TLB)或者多级页表来提高访问效率。

        多级页表如果是所有页表都不存在的话,实际存储空间并没有减少。但是在实际使用中,进程不需要使用全部地址空间,有些页表可以暂时不存在。比如都过存在位表示某个页表不存在,这样是可以有效减少页表的存储。

        下面是一个二级页表的示例。一级页表的页表项是二级页表的起始页号,再加上偏移就可以得到实际物理地址。

段页式存储管理

        该种方法式段式和页式的结合。每个逻辑地址由段号,页号以及页内偏移地址组成。

        首先会根据段号在段表中找到段表项。根据段表项找到页号,根据页号在对应页表(根据页表起始位置寻址)中找到物理存储块,根据偏移地址算出实际物理地址。

虚拟存储

        上面讲了段式、页式、段页式各种内存空间分配方法,但是他们都会存在一个问题,即物理内存不够,他们都需要一次性全部加载到内存中。虚拟存储就是为了解决这个问题,它会利用外存,且允许部分程序和数据装载到内存中,并通过缺页异常完成页面置换等手段,解决物理内存不够的问题。

        可能,你会想内存和外存这种不断装载,不怕性能差吗?这主要是利用了局部性原理,即时间局部性(一条指令前后执行时间较短),空间局部性(前后指令几乎在某个部分区域),分支局限性(一条跳转指令的多次执行,会跳到同一个位置)。

虚拟存储的概念:

        1、在装载程序时只将部分需要的程序和数据放到内存中;

        2、在执行时如果遇到需要的数据不在内存中,操作系统会将程序或数据放到内存中;

        3、操作系统会将内存中用不到的内存或数据放到外存中。

那对于2,3步骤系统是怎么做的呢?

        第2步主要是通过缺页异常请求,它使用的是虚拟页式存储。它相对于页式存储管理,区别是它不会一次性把所有页表项都加载到内存中,只会加载一部分,通过设置标志位决定是否有效。

        当我们执行CPU指令时,会根据页号找到对应页表项,如果无效,就会抛出缺页异常,这时会触发操作系统的进程,会将外存的页加载进来。如果加载过程发现没有空闲物理页了,会执行页面置换,把某个页面移到外部去。如果目标页面修改位有效,还要将其更新到外存。

        上面提到了置换,主要通过操作系统的页面置换算法来实现。目前提供了几种比较常用的算法,比如FIFO,LRU,OPT,CLOCK等等。

        OPT:是一种最优页面置换算法。即每次都算出每个页面的下次访问时间。这是一种理想化的算法,实际是上是无法实现的,因此无法预知逻辑页面的等待时间。

        FIFO:最简单的算法,根据先入先出来决定置换,但这种算法并不能反映真实的程序执行情况。操作系统会维护一个逻辑页面的链表,缺页的时候会直接从链首取。

        LRU:这个在其他地方经常遇见,最近最少访问,会找到最近最少访问的页面,进行置换。

        CLOCK:记录页面访问的大致情况。它会将整个页面做成圆形链表。在访问页面时,会将页标项的访问位置为1。缺页时,会从当前位置扫描链表,找到访问位为0的,遇到1时,会将访问位置为0,然后接着找,直到遇到为0的进行置换。

内存回收

        内存管理不得不提到内存淘汰机制。Linux也会对内存进行回收。

        其基本流程是当内存不够用时,会进行内存回收,内存回收包括后台回收和直接内存回收,后台回收会启动一个特殊线程,叫kswapd ,如果后台回收仍然不够时或者说后台回收的速度赶不上内存申请的速度,就会触发直接内存回收,该过程的回收时同步的方式进行的。如果还是不够内存会首先kill掉占用内存大的进程,如果还是不够,直接就OOM。

        后台回收会定期扫描内存页,会通过不同的阈值来触发后台内存回收。

        内存回收的对象主要是文件页和匿名页。

        文件页(file):之前在介绍零拷贝技术时,说到了PageCache,为了加快速度,会将文件数据缓存到内存中,这部分数据就是文件页缓存。回收的方式是,如果是干净页,直接释放掉对应缓存;如果是dirty,要先将页数据写回到磁盘中,再释放掉对应的缓存。

        匿名页(anon):有些数据并不是文件缓存的数据,而是进程产生的,比如堆、栈等数据,这部分数据暂时用不上,但未来可能还会用,会先将其放到磁盘中,未来可能的时间再加载到内存中。这是通过Linux的Swap机制实现的。

        针对这两种不同的页,分别维护了两个链表,一个是活跃链表(active_lists),一个是不活跃链表(inactive_list),这是一种改进的LRU算法。如果访问数据不在链表中,则直接加入到不活跃链表中,如果数据第二次被访问到,会加入到活跃链表。在进行内存淘汰时,直接淘汰不活跃链表的数据即可。

haibo@haiboInxiaomi:~$ cat /proc/meminfo |grep -i Active
Active:          2716196 kB
Inactive:        8889912 kB
Active(anon):       2500 kB
Inactive(anon):  4911780 kB
Active(file):    2713696 kB
Inactive(file):  3978132 kB

        通过两张链表可以有效避免了预读失效和缓存污染的问题。

        预读失效:我们都知道,程序具备局部性原理,在读取存储器数据时,是以页或者块为单位进行读取的,一次会读取连续空间的数据,即时有些数据本次不会用到,Linux默认的页是4KB ,当然页可以配置大页( Huge pages,2MB或者1G),Mysql页(Innodb)的大小是16KB。但在极端情况下,可能会出现一个问题,就是预读的数据永远都不会被访问到。如果是利用传统的LRU,访问就加到链表头部的话,可能会导致很多热点数据被挤到链表尾部,导致热点数据可能会被淘汰。当然,还要提到一点,由于局部性原理,预读失效的概率并不大。

        Linux通过两个队列,首先将数据预读数据放到不活跃链表,尽可能缩短了不活跃的预读数据缓存时间,首先被淘汰。

        缓存污染:缓存污染指的是大量数据占据了链表头部,导致热点数据被淘汰。其实也是预读引起的。对于上面的链表,预读出来的数据是放到不活跃链表,如果被访问到就直接放到活跃链表中,就仍然存在缓存污染的危险。针对这个问题,linux的做法是只有第二次被访问到,才会被加入到活跃链表中,即提高了加入活跃链表中的门槛。在一定程度上,有效地降低了缓存污染的问题。

参考资料:

操作系统自学笔记:内存管理

操作系统知识盘点

操作系统-段式存储管理

操作系统内存管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/710001.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

07-Linux部署Nginx

Linux部署Nginx 简介 NGINX是一款高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。它的特点包括占用内存少、并发能力强,因此在处理高负载和高并发的场景时表现优秀。NGINX由俄罗斯的程序设计师Igor Sysoev开发,最初是为俄…

Windows Docker 部署 SQL Server

部署 SQL Server 打开 Docker Desktop,切换到 Linux 内核。然后在 PowerShell 执行下面命令,即可启动一个 SQL Server 服务,这里安装的是 2022 年版本 docker run -e "ACCEPT_EULAY" -e "MSSQL_SA_PASSWORDSQL123abcABC!&qu…

Spring 事务传播机制

事务传播机制:多个事务⽅法存在调⽤关系时, 事务是如何在这些⽅法间进⾏传播的。 ⽐如:有两个⽅法A,B都被 Transactional 修饰,,A⽅法调⽤B⽅法 A⽅法运⾏时, 会开启⼀个事务。当A调⽤B时, B⽅法本⾝也有事务&#xf…

59.仿简道云公式函数实战-文本函数-RMBCAP

1. RMBCAP函数 RMBCAP 函数可以将金额小写转换为人民币大写金额形式。 2. 函数用法 RMBCAP(数字) 3. 函数示例 如,在财务结算、报销管理、对公付款等场景中,可以利用 RMBCAP 函数将金额转换为大写,避免被篡改产生的负面影响 4. 代码实战…

Socket网络编程(一)——网络通信入门基本概念

目录 网络通信基本概念什么是网络?网络通信的基本架构什么是网络编程?7层网络模型-OSI模型什么是Socket?Socket的作用和组成Socket传输原理Socket与TCP、UDP的关系CS模型(Client-Server Application)报文段牛刀小试(TCP消息发送与接收&#…

江科大stm32学习笔记——【5-2】对射式红外传感器计次旋转编码计次

一.对射式红外传感器计次 1.原理 2.硬件连接 3.程序 CountSensor.c: #include "stm32f10x.h" // Device header #include "Delay.h"uint16_t CountSensor_Count;void CountSensor_Init(void) {//配置RCC时钟:RCC_APB2Perip…

前端架构: 脚手架之包管理工具的案例对比及workspaces特性的使用与发布过程

npm的workspaces 特性 1 )使用或不使用包管理工具的对比 vue-cli 这个脚手架使用 Lerna 管理,它的项目显得非常清晰在 vue-cli 中包含很多 package 点开进去,每一个包都有package.json它里面有很多项目,再没有 Lerna 之前去维护和…

SpringCloudNacos注册中心服务分级存储模型

文章目录 服务分级存储模型概述配置集群同集群优先的负载均衡 权重配置总结 之前对 Nacos注册中心入门 已经做了演示. 这篇文章对 Nacos 的服务分级存储模型做理论与实践. 服务分级存储模型概述 一个服务可以有多个实例,例如我们的 user-server,可以有:…

C#使用iText7给PDF文档添加书签

上一篇文章将SqlSugar官网文档中每个链接对应的网页生成独立PDF文档再合并为单个PDF文档,但是没有书签,八百多页的内容查找和跳转都不方便,本文学习和使用iText7给PDF文档添加多级书签。   添加多级书签分为两大步骤:1&#xff…

VR全景HDR拍摄教程

什么是HDR? HDR可以用在哪里? 书面解释:HDR(高动态范围 High Dynamic Range)摄影,是摄影领域广泛使用的一种技术。 是不是有点懵? 我们来看一个实际的拍摄现场环境,你就懂了 我们…

使用 Gradle 版本目录进行依赖管理 - Android

/ 前言 / 在软件开发中,依赖管理是一个至关重要的方面。合理的依赖版本控制有助于确保项目的稳定性、安全性和可维护性。 Gradle版本目录(Version Catalogs)是 Gradle 构建工具的一个强大功能,它为项目提供了一种集中管理依赖…

定时任务框架

定时任务的框架有哪些 ● Timer,JDK自带的,比较简单,使用的时候,定义一个TimerTask,实现run方法,然后定义一个Timer类,调用timer.schedule(timerTask,1000,3000); ○ 缺点:单线程、…

附加Numpy数组

参考:Append Numpy Array 引言 在数据科学和机器学习领域,处理大规模数据集是一项重要且常见的任务。为了高效地处理数据,numpy是一个非常强大的Python库。本文将详细介绍numpy中的一个重要操作,即如何附加(append&a…

LeetCode:2867. 统计树中的合法路径数目(筛质数+ DFS Java)

目录 2867. 统计树中的合法路径数目 题目描述: 实现代码与思路: 筛质数 DFS 原理思路: 2867. 统计树中的合法路径数目 题目描述: 给你一棵 n 个节点的无向树,节点编号为 1 到 n 。给你一个整数 n 和一个长度为 …

西软云XMS operate XXE漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

Jquery操作DOM对象

文章目录 目录 文章目录 本章目标 一.DOM操作分类 二.JQuery中的DOM操作 内容操作 属性值操作 节点操作 节点属性操作 节点遍历 总结 本章目标 使用Jquery操作网页元素使用JQuery操作文本与属性值内容使用JQuery操作DOM节点使用Jquery遍历DOM节点使用JQuery操作CSS-DOM 一…

WebServer -- 日志系统(下)

目录 🌼整体思路 🎂基础API fputs 可变参数宏 __VA_ARGS__ fflush 🚩流程图与日志类定义 流程图 日志类定义 🌼功能实现 生成日志文件 && 判断写入方式 日志分级与分文件 🌼整体思路 日志系统分两部…

无法调试MFC源码

VS无法调试MFC源码 起初 有时候就是这么无奈,MFC源码各种问题没有办法调试,可是又想看下代码如何调用,里面做了些什么,从哪儿调出,学习一下大神的思路什么的。整理一下有可能的原因。 检查生成代码设置 需要设置正…

04 Opencv图像操作

文章目录 读写像素修改像素值Vec3b与Vec3F灰度图像增强获取图像通道bitwise_not 算子对图像非操作 读写像素 读一个GRAY像素点的像素值(CV_8UC1) Scalar intensity img.at(y, x); 或者 Scalar intensity img.at(Point(x, y)); 读一个RGB像素点的像素值…

虚拟机看不到共享文件夹

johnjohn-virtual-machine:/mnt/hgfs$ cat /etc/issue Ubuntu 20.04.6 LTS \n \l 看下是否挂载 johnjohn-virtual-machine:/mnt/hgfs$ vmware-hgfsclient FPGAs_AdaptiveSoCs_Unified_2023.2_1013_2256 xilinx 取消挂载 johnjohn-virtual-machine:/mnt/hgfs$ sudo umount /mn…