内存管理 | 进程地址空间

1.进程地址空间的理解

在 前文 分享的fork创建子进程的系统调用中,一个变量接收了两个不同的返回值!通过推测也知道,那个地址绝不是真是的地址(物理地址),而是一个虚拟的地址!

在操作系统中,进程所用的地址是独立的一套虚拟地址,系统通过一套机制,将虚拟地址映射到真正存储数据的物理地址!其实在单片机或是早期的计算机都是直接操作物理地址地址的,但是如果一台计算机有两个不同的进程,访问同一个物理地址,那么就会造成程序崩溃,虚拟地址的内存管理机制就避免了这种情况,让多进程运行变成了可能!

在这里插入图片描述

  • 我们程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address)
  • 实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)

操作系统中通过CPU中的内存管理单元(MMU 来将虚拟地址映射到物理地址当中,然后再通过物理地址访问内存

在这里插入图片描述

如何理解

虚拟化技术是操作系统的惯用手法,为进程运行分配很短(毫秒级)的时间片,让人感绝自己的程序一直运行;操作系统运行虚拟地址空间(进程地址空间),为进程营造一种,计算机的所有内存都被自己(每一个进程)占有!在32位的机器上,4G的空间都是每个进程独占的,进程之间感受不到彼此的存在!

在这里插入图片描述

举一个生活的例子:

一个富豪有十亿美金,有两个私生子,分别叫张三,李四,每个私生子之间都不知道有彼此的存在!张三喜欢学习,李四喜欢跑步!

一天富豪就分别对两个儿子说:张三啊,你好好学习,争取拿个诺贝尔奖,我以后十个亿的家产都是你的;同样的话又对李四说,李四啊,你好好跑步,争取拿个奥运冠军,我以后十个亿的家产都是你的。

我们能看出来,这个富豪,是个画家!从例子类比到计算当中:

富豪:操作系统 ; 私生子:一个一个进程

富豪画的:进程地址空间(注意名词:饼) ; 生活中真正的饼:物理空间

张三或李四之间感受不到彼此的存在,如果他们向富豪要100万,没问题!但是给十个亿,那显然富豪给不了!

对比到操作系统也是一样,一个进程不可能独占4G,4G有1G的内核空间,还有系统的那么多其他进程,但是系统能通过这种方式很好的管理(先描述,再组织)!在Linux中进程地址空间/虚拟地址空间:就是内核管理的数据结构(mm_struct结构体)!

struct task_struct {struct mm_struct *mm, *active_mm;
}
struct mm_struct 
{...unsigned long total_vm, locked_vm, shared_vm, exec_vm;unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;...
}

这里我们就可以理解fork一个变量就收两个返回值

子进程的创建会以父进程作为模板,进行拷贝,对于一些代码和只读的数据父子进程共享,对于需要修改的数据,系统使用写时拷贝的技术,将需要修改的数据拷贝一份然后再对其进行修改。

这就解释了,为什么一个虚拟地址或变量有两个不同的值!因为同一个虚拟地址,不同的进程映射到了不同的物理地址!通过虚拟内存 + 页表映射的方式和一些写时拷贝的技术保证了进程的独立性!

在这里插入图片描述

2.将虚拟地址转换为物理地址

这一段内容我几乎是搬运小林哥的博客!

操作系统如何将虚拟地址转化成物理地址

将虚拟地址转化成物理地址主要有这两种方式:内存分段内存分页,Linux系统中采用的是内存分页的方式将虚拟内存映射到物理内存当中,但两种都是通过起始地址 + 偏移量的方式转换

内存分段

在这里插入图片描述

虚拟地址分为段选择因子和段内偏移量,在段选择因子当中可以通过段号来找到段表中的段基地址,从而获取物理地址的起始地址,再通过偏移量,找到对应的物理地址!

内存分段的不足:

  • 第一个就是内存碎片的问题。
  • 第二个就是内存交换的效率低的问题。

内存碎片主要分为,内部内存碎片和外部内存碎片

外部内存碎片

在这里插入图片描述

内部内存碎片

在计算机中,会通过内存对齐的方式,来提高计算机读取的效率,比如:实际的数据是5字节,但是根据内存对齐分配了8字节,剩下的3个字节就是内部碎片!

再堆的管理当中堆内存碎片是管理的重点,malloc在一定的场景下上会有许多的内存碎片问题!

内存交换

内存分段管理可以做到段根据实际需求分配内存,所以有多少需求就分配多大的段,所以不会出现内部内存碎片。但是由于每个段的长度不固定,所以多个段未必能恰好使用所有的内存空间,会产生了多个不连续的小物理内存,导致新的程序无法被装载,所以会出现外部内存碎片的问题。

可以通过内存交换的方式解决外部碎片问题:

将一些程序换出(挂起),在重新组织换入;所谓的换出就是将程序换出到swap分区中,到但是swap是磁盘会有效率的消耗。

内存分页

分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫Page)。在 Linux 下,每一页的大小为 4KB

在这里插入图片描述

页表是存储在内存里的,内存管理单元 (MMU)就做将虚拟内存地址转换成物理地址的工作。

而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

虚拟内存如何通过分页转换成物理内存

在这里插入图片描述

步骤:

  • 把虚拟内存地址,切分成页号和偏移量;
  • 根据页号,从页表里面,查询对应的物理页号;
  • 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。

简单的分页的缺陷

在 32 位的环境下,虚拟地址空间共有 4GB,假设一个页的大小是 4KB(2^12),那么就需要大约 100 万 (2^20) 个页,每个「页表项」需要 4 个字节大小来存储,那么整个 4GB 空间的映射就需要有 4MB 的内存来存储页表。

这 4MB 大小的页表,看起来也不是很大。但是要知道每个进程都是有自己的虚拟地址空间的,也就说都有自己的页表。

那么,100 个进程的话,就需要 400MB 的内存来存储页表,这是非常大的内存了,更别说 64 位的环境了。

多级页表

我们把这个 100 多万个「页表项」的单级页表再分页,将页表(一级页表)分为 1024 个页表(二级页表),每个表(二级页表)中包含 1024 个「页表项」,形成二级分页。如下图所示:

在这里插入图片描述

如果使用了二级分页,一级页表就可以覆盖整个 4GB 虚拟地址空间,但如果某个一级页表的页表项没有被用到,也就不需要创建这个页表项对应的二级页表了,即可以在需要时才创建二级页表。做个简单的计算,假设只有 20% 的一级页表项被用到了,那么页表占用的内存空间就只有 4KB(一级页表) + 20% * 4MB(二级页表)= 0.804MB,这对比单级页表的 4MB 是不是一个巨大的节约!

程序是按需加载的,在32位机器上不然小小的4G内存,怎么能装的下几十G的游戏!

3.进程地址空间的设计

虚拟地址在什么时候就有呢,在程序加载到内存之前有没有地址

可以通过一些反汇编的工具来,查看,在编译好了的程序,就存在了虚拟地址!

objdump -afh 可执行程序

结果:编译器和系统之间用的是同一套进程地址空间的设计方案,这样编译器和操作系统才能配合工作。

在这里插入图片描述

CPU在执行指令的时候用的是什么样的地址

虚拟地址

在这里插入图片描述

4.进程地址空间的好处

1.防止非法访问

当非法的访问或映射是,操作系统都或识别并终止你的进程,从而保护了物理内存。进程地址空间和页表都是由操作系统创建并维护的,也就意味着使用地址空间和页表进行映射都在操作系统的监管之下,如果你是非法的,操作系统就会终止你的进程,这也说明了进程的崩溃,就是操作系统杀死了你的进程!

2.内存和进程管理模块解耦

有了进程地址空间和页表映射的存在,我们的数据(数据+代码)可以映射到任意的物理内存的位置!这就说明了,内存管理模块和进程管理模块可以做到没有关联的关系,做到了解耦合!

所有,在C、C++语言上使用malloc函数和new的时候是在虚拟内存空间上申请的,或也可是说是在进程地址空间上申请的。虽然在地址空间上申请了空间,但是在物理内存甚至一个字节都不给你!

因为我们申请的空间有是都并不会直接到,当你真正的对物理内存进程访问的时候,才会执行相关的内存管理算法,帮你申请内存。

这是使用到了延迟分配的策略,来提高整机的效率,这是由操作系统来自动完成的,对用户的继承来说,完全是零感知的。

3.内存分布有序化

物理内存可以加载到任何一个地方,这样几乎所有的代码和数据都是乱序的!而乱序的东西是不便于管理的!通过页表的存进可以将虚拟内存和物理内存一一映射,站在进程的角度上所有的内存都是有序的,便于管理。

请的。虽然在地址空间上申请了空间,但是在物理内存甚至一个字节都不给你!

因为我们申请的空间有是都并不会直接到,当你真正的对物理内存进程访问的时候,才会执行相关的内存管理算法,帮你申请内存。

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

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

相关文章

设计模式(创建型模式)工厂模式

目录 一、简介1.1、接口定义1.2、调用 二、简单工厂模式2.1、解析工厂2.2、调用 三、工厂方法模式3.1、解析器接口定义3.2、解析工厂接口定义3.3、解析器工厂的工厂3.4、调用 四、抽象工厂模式4.1、内容解析器4.2、工厂类 三、优点与缺点 一、简介 工厂模式我将它分为三类&…

什么是系统工程(字幕)23

0 00:00:00,000 --> 00:00:01,617 那这里没有出现 1 00:00:01,617 --> 00:00:04,448 我们可以把它从这里再拖上来 2 00:00:04,448 --> 00:00:06,267 但是这个地方不够 3 00:00:06,267 --> 00:00:08,491 我们把这个地方放大一点 4 00:00:08,491 --> 00:00:11…

分享66个相册特效,总有一款适合您

分享66个相册特效,总有一款适合您 66个相册特效下载链接:https://pan.baidu.com/s/1jqctaho4sL_iGSNExhWB6A?pwd8888 提取码:8888 Python采集代码下载链接:采集代码.zip - 蓝奏云 学习知识费力气,收集整理更不…

【java苍穹外卖项目实战二】苍穹外卖环境搭建

文章目录 1、前端环境搭建2、后端环境搭建1、项目结构搭建2、Git版本控制3、数据库创建 开发环境搭建主要包含前端环境和后端环境两部分。 前端的页面我们只需要导入资料中的nginx, 前端页面的代码我们只需要能看懂即可。 1、前端环境搭建 前端运行环境的nginx&am…

中科星图GVE(AI案例)——如何利用ai技术进行船只的识别和提取分析(珠江三角洲)

简介 随着人工智能(AI)技术的快速发展,船只的识别和分析变得越来越容易。AI技术可以利用计算机视觉和深度学习算法来自动识别和提取船只的特征,从而进行进一步的分析。下面将介绍如何利用GVE云计算平台的AI技术进行船只的识别和提取分析。 1. 数据收集与准备: 首先,需要…

STM32 FSMC (Flexible static memory controller) 灵活静态内存控制器介绍

文章目录 1. 介绍FSMC2. FSMC特点3. Block示意图4. AHB接口4.1 Supported memories and transactionsGeneral transaction rulesConfiguration registers 5. 外部设备地址映射5.1 NOR/PSRAM地址映射将NOR Flash/PSRAM的支持进行封装 5.2 NAND/PC Card地址映射 1. 介绍FSMC 说到…

使用python-numpy实现一个简单神经网络

目录 前言 导入numpy并初始化数据和激活函数 初始化学习率和模型参数 迭代更新模型参数(权重) 小彩蛋 前言 这篇文章,小编带大家使用python-numpy实现一个简单的三层神经网络,不使用pytorch等深度学习框架,来理解…

TELNET 远程终端协议

远程终端协议 TELNET TELNET 是一个简单的远程终端协议,也是互联网的正式标准。 用户用 TELNET 就可在其所在地通过 TCP 连接注册(即登录)到远地的另一个主机上(使用主机名或 IP 地址)。 TELNET 能将用户的击键传到…

使用cocos2d-console初始化一个项目

先下载好cocos2d-x的源码包 地址 https://www.cocos.com/cocos2dx-download 这里使用的版本是 自己的电脑要先装好python27 用python安装cocos2d-console 看到项目中有个setup.py的一个文件 python setup.py 用上面的命令执行一下。 如果执行正常的话回出现上面的图 然后…

法国实习面试——计算机相关专业词汇

法语 1.Spcialit - 专业 2.Systme - 系统 3.Embarqus - 嵌入式 4.Logicielle - 软件 5.Distribus - 分布式 6.lectronique - 电子 7.nergie lectrique - 电能 8.Automatisation - 自动化 9.Une exprience de stage - 实习经验 10.Automobiles - 汽车 11.tre charg…

每日五道java面试题之java基础篇(二)

第一题. 为什么说 Java 语⾔“编译与解释并存”? ⾼级编程语⾔按照程序的执⾏⽅式分为编译型和解释型两种。 简单来说,编译型语⾔是指编译器针对特定的操作系统将源代码⼀次性翻译成可被该平台执⾏的机器码;解释型语⾔是指解释器对源程序逐…

前端JavaScript篇之call() 和 apply() 的区别?

目录 call() 和 apply() 的区别? call() 和 apply() 的区别? 在JavaScript中,call()和apply()都是用来改变函数中this指向的方法,它们的作用是一样的,只是传参的方式不同。 call()方法和apply()方法的第一个参数都是…

4核8g服务器能支持多少人访问?2024新版测评

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线?通用型-4核8G-180G-2000G,2000GB月流量,系统盘为180GB SSD盘,12M公网带宽,下载速度峰值为1536KB/s,即1.5M/秒,假设网站内页平均大小为60KB…

js-添加网页快捷方式

title: js-添加网页快捷方式 categories: Javascript tags: [p快捷方式] date: 2024-02-04 15:28:25 comments: false mathjax: true toc: true js-添加网页快捷方式 前篇 谷歌上包困难的情况, 只能通过投放落地页来缓解一下痛苦, web2app 那种形式有几个比较大的缺点就是需要…

MongoDB部署策略

内 容 简 介 本文介绍了MongoDB数据库的优点的数据存储模式的安装部署过程。 利用MongoDB在存储海量数据上的优势,部署存储空间大数据。 欢迎批评指正补充 由于编者水平有限,所搜集资料也很有限,制定的规范肯定有考虑不周全、甚至完全错误…

【Android】GridLayout实现等比布局

GridLayout是十年前的老布局了&#xff0c;因为太久没用过&#xff0c;导致细节方面已经记不太清楚了 初衷是想让GridLayout中每行每列的元素大小都相等&#xff0c;结果怎么试都没有效果 原来是要只设置比例&#xff0c;不设置宽高才行&#xff0c;特意记录下 <GridLayou…

K8S容器挂了后一直重启但不成功原因及解决

K8S容器挂了后一直重启但不成功原因及解决 背景&#xff1a; 应用迁移K8S后&#xff0c;因为POD OOM挂了后&#xff0c;会进行重启&#xff0c;但一直在重启&#xff0c;重启多次都不成功&#xff0c;且应用无报错日志。其他K8S上应用挂了后会自动重启成功。 原因&#xff1…

速过计算机二级python——第9讲 详解第 2 套真题

第9讲 详解第 2 套真题 基本编程题【15 分】简单应用题【25 分】综合应用题【20 分】**问题 1**【10 分】:**问题 2【10 分】:**基本编程题【15 分】 考生文件夹下存在一个文件 PY101.py,请写代码替换横线,不修改其他代码,实现以下功能:【5 分】题目: import __________ b…

指针的学习3

目录 字符指针变量 数组指针变量 二维数组传参的本质 函数指针变量 函数指针变量的创建 函数指针变量的使用 两段有趣的代码 typedef关键字 函数指针数组 转移表 回调函数&#xff1a; 字符指针变量 int main() {char arr[10] "abcdef";char* p1 arr;//…

day36 作用域、作用域链

目录 作用域局部作用域函数作用域块作用域 全局作用域作用域链 作用域 了解作用域对程序执行的影响及作用域链的查找机制&#xff0c;使用闭包函数创建隔离作用域避免全局变量污染。 作用域&#xff08;scope&#xff09;规定了变量能够被访问的“范围”&#xff0c;离开了这个…