进程地址空间
目录
回顾C/C++语言的程序地址空间
感性认识虚拟地址空间
虚拟地址空间与物理空间如何建立映射关系
为什么要虚拟地址空间?
回顾C/C++语言的程序地址空间
在学习C/C++语言时我们知道了一个概念叫程序地址空间。通俗来说就是如下一张表,从中可以得知系统的几个区域:

现在有个问题,这个表是内存吗?来看个例子就知道了:


可以看到,一开始父进程和子进程对应打印的全局变量数值和地址都一样,奇怪的事情出现了:当全局变量的值在子进程中被改变后,父进程的变量地址和子进程的地址还是同一个,但是两个的值不一样!
这是不可想象的,之前讲进程的时候说了,进程具有独立性,多进程下进程间是互不干扰的,即使共用代码但也会根据条件判断语句分开执行。 一个物理地址对应的数据流只有一个,对应的值也只有一个,但是这里确实出现了一个地址对应两个不同值的情况。这只能说明一个事实:这里的地址不是物理地址!
这里的地址叫做虚拟地址,也叫线性地址或逻辑地址。这就回答了上面的问题,那不是物理意义的内存,而是虚拟内存,所以以前学语言的时候说的指针不是物理地址,而是虚拟地址。
感性认识虚拟地址空间
进程在运行的时候会有一个错觉,它觉得它是独占CPU资源的,实际上在我们看来根本不是这样,CPU每时每刻都在调度不同进程,不断进行着切换。
根据这一点我举个例子帮助感性理解:
有个大帮派势力暗中统治着几座城市,帮主手下有两个心腹,但是他们俩互相不知道对方的存在。
为了让两个心腹好好干活别想着觊觎帮主之位,帮主对心腹A说,我死后这个集团就交给你打理了;同时,他也对心腹B说了一样的话,相当于分别给他们俩画了个大饼,而他们两人都不知道帮主和另一个人也许下了承诺。于是两人好好干,有一天A对帮主说想要更多实权,帮主说反正我快走了到时候一切都交给你,但你现在先别急,于是A愉快地干活去了,B也经历了同样的事。
这里帮主就好比操作系统,两个心腹A,B就是进程,两个进程以为自己独占着操作系统资源,操作系统为了更好地调度他们,给他们画了大饼,这张饼也就是”进程地址空间“,当进程想要向操作系统多要点空间资源时,操作系统不能一次全给它(本身内存吃紧要节省空间)就会分批一次一次给空间——对应我们malloc/new开空间。
那么操作系统是如何画饼的呢?————还是类比刚刚的例子,实际上画饼就是在人的脑中构建蓝图,可以看成一个数据结构:
struct 蓝图
{char* who;char* when;char* target;
};
那么操作系统也是一样,是对进程地址空间做管理,管理的本质是先描述再组织,描述也一样用struct结构体描述起来,地址空间的本质就是内核的数据结构mm_struct
地址空间上有heap,stack等区域,那么操作系统是怎么在地址空间上将它们描述起来的呢?————下面再举个例子:
帮主的帮派很大管理着几个城市,但是同等大小的势力也存在,并和该帮派对抗着。为了不引起大规模的乱斗,两个帮派划地分界,谁都不准越界半步。那么用数据结构进行区域划分是这样的:
struct area //区域
{unsigned int A_start; //帮派A区域起始位置unsigned int A_end; //帮派A区域结束位置unsigned int B_start; //帮派B区域起始位置unsigned int B_end; //帮派B区域结束位置
};struct area partition = {1,50,51,100};
//帮派A区域 [1,50]
//帮派B区域 [51,100]
两个帮派定好地界后还是有问题,因为货物运输交易经常牵扯到边界附近地带,容易造成摩擦,所以经过商量将小镇T作为公共使用区域,两边都可涉足:
struct area partition = {1,45,55,100};
这是区域调整的方法。
类比以上,操作系统也是一个道理。
地址空间描述的基本空间大小是字节,32位下是2^32个地址,1个地址1字节,一共大概4GB空间。每一个字节都要有唯一的地址。

如此一来,操作系统就可以根据划分好的地址描述heap等区域:
struct mm_struct
{unsigned int code_start;unsigned int code_end;unsigned int stack_start;unsigned int stack_end;.......
};

假设mm_struct这个内核数据结构就是这样的,里面区域划分假定如上,总之默认占4GB大小。
code_start 到 code_end这段区域里面有很多地址,叫做虚拟地址。
想要调整区域,像例子中的设定公共区域,其实就是改变区域的起始地址和结束地址,比如要扩大stack区域,可以增加stack_end。我们写C/C++代码时,malloc/new空间实际上就是扩大堆区,free就是缩小堆区。
我们知道,进程在创建的时候操作系统会创建一个PCB内核数据结构task_struct,里面存了进程的相关信息如优先级...然后操作系统给进程 “画大饼” ,进程为了得到这块大饼,它的task_struct里面有个指针指向mm_struct。
虚拟地址空间与物理空间如何建立映射关系
这里牵扯到页表等相关概念,我们处理一下简单讲解。
我们已经知道磁盘上的程序会加载到内存,并且一字节对应内存上的一个物理地址空间。

假设mytest.exe程序大小是8KB,内存起始地址是0x1111 1111,那么虚拟进程地址空间是如何与物理内存建立映射关系的呢?————系统中有一个东西叫页表,可以建立映射。

关于页表,页等概念这里先不说,主要看进程地址空间。
虚拟地址空间的一个字节0xFF01 EEEC存到页表左边,与右边存物理地址空间的0x2100 1110对应。 假设我们写代码int a = 10; 这里&a就是虚拟地址,会对应虚拟进程地址空间的一个字节,再对应页表的数据,再与物理空间对应。当修改a的值,物理内存存储的值也被改变。
补充:
1、在Linux下,我们认为虚拟地址和线性地址是一个概念(在其它某些OS下不一样)。因为2^32个虚拟地址都是紧挨一起线性排布下去的。
2、再次画图理解整个过程

每个进程都有自己的task_struct和mm_struct以及页表,当然物理内存只有一个,通过上述方式建立联系。操作系统OS操控管理着一切,给进程 “画大饼”,让进程看似好像独享操作系统资源,坐拥2^32字节大小空间,但其实进程无法直接查看物理内存大小和占用情况,只能通过 mm_struct ---> 页表---->物理内存的方式,而进程胃口也比较小,一次开个10MB.100MB很多了,再多OS也不会再给进程空间了。
为什么要虚拟地址空间?
1、虚拟地址空间可以保护物理空间不被错误修改,提高了系统的安全性。
试想一下要是没有虚拟地址空间,用户直接访问物理空间会发生什么。
假设你想修改一个数据,但是你访问错位置了,将一块重要的数据给修改了;或者你写的程序出现野指针等错误,容易造成系统的崩溃。

刚刚提到的页表除了建立虚拟空间地址和物理内存的映射关系外,还有其他作用,其中之一是保护与拦截。
当进程做出违反操作系统的行为访问物理内存时,页表会进行拦截。因为进程不能直接访问物理内存,需要通过mm_struct--->页表,此时页表就可以拦截越界行为,保护物理内存。

2、虚拟进程地址的存在,可以更方便的进行进程与进程数据代码的解耦,保证进程的独立性。
这段话很抽象,我们用例子来理解。还记得上面父子进程的那个程序吗,在子进程内部改变了全局变量globol_val,子进程和父进程打印的变量数值不同但虚拟地址相同。


现在我们结合刚刚获取的知识再来深入理解一下为什么虚拟地址相同。

系统生成父进程后,再生成子进程,子进程是以父进程为模板构建出来的,tast_struct , mm_struct,页表都一样。它们里面的全局变量globol_val的虚拟地址都依据映射指向物理内存的同一地址,所以一开始执行的时候打印的变量数值一样地址也一样。
当子进程内想要改变全局变量globol_val, 要经过虚拟地址、页表访问物理内存,此时操作系统会做一个工作——拷贝物理内存对应位置的数据给它另一块地址,然后更改页表映射,将子进程虚拟地址映射指向新的物理内存地址,最后更改新的globol_val的值。

为什么操作系统要做这一步?————因为进程具有独立性,当一个进程对被共享的数据进行修改时会破坏独立性,影响其他共享数据的进程,所以为了让进程改变数据的同时不破坏独立性,操作系统将该进程的页表映射更改,所以产生了虚拟地址相同,但对应值不同的现象。
这里的拷贝方式叫做写时拷贝,它可以让不同进程的数据分离。

3、 虚拟地址空间可以让进程以统一的视角来看待进程对应的代码和数据等各个区域,方便使用。
编译器也是以同样的视角来编译代码(地址)。
问题:可执行程序内部代码有地址吗?————有。进入汇编可以查看代码对应的地址,并且在链接的过程中也需要代码地址与库链接。
既然可执行程序代码有地址,那这个地址是什么地址呢?是物理地址还是虚拟地址?————自然是虚拟地址,或者这里更准确的说应该叫逻辑地址。

磁盘上的可执行程序里面有一个main函数,其内部调用了fun函数,并且fun函数的地址是0x1122,main函数的地址是0xFEE0, 程序也有全局数据区,代码区等,32位平台下和上面讲的虚拟地址空间编址方式一样是以32位编址的。
可执行程序加载到内存,天然地就有了一个外部的物理地址,和程序内部编址方式一样的。在程序内部main函数寻址调用fun函数,外部物理内存也是一样通过物理寻址main函数调用fun函数。
所以我们现在有两套地址,标识物理内存中代码和数据的地址,还有在程序内部进行跳转的虚拟地址。

内存将进程数据加载到CPU,CPU收到数据包括main函数和fun函数的地址(注意:此时CPU接收到的地址是虚拟地址!因为CPU读取的是指令,指令内部就有地址),PC指针记录下fun函数的地址,CPU先拿着main函数的虚拟地址,到页表查表映射物理内存,找到对应的main函数的物理地址然后执行main函数。
这里有个问题:CPU执行出来的地址是物理地址还是虚拟地址?————是虚拟地址,虚拟地址通过页表映射到物理内存。再调用main函数内部的fun函数,根据PC指针保存的fun函数地址,CPU再通过页表映射物理内存,此时CPU出来的还是虚拟地址。这样就形成了一个循环。

CPU在这整个过程中没有见到过物理地址,全都是虚拟地址。
相关文章:
进程地址空间
目录 回顾C/C语言的程序地址空间 感性认识虚拟地址空间 虚拟地址空间与物理空间如何建立映射关系 为什么要虚拟地址空间? 回顾C/C语言的程序地址空间 在学习C/C语言时我们知道了一个概念叫程序地址空间。通俗来说就是如下一张表,从中可以得知系统的几…...
数楼梯(加强版)
数楼梯(加强版) 题目背景: 小明一天放学回家,看到从1楼到2楼共有n个台阶,因为好奇,他想尝试一下总共有几种方案到二楼?他可以1步,2步,3步的跳,不能跳3步以上. 他试了很多次都没有解决这个问题,于是请求聪明的你帮忙解决这个问题. 题目描述: 1楼到2楼楼梯有n级台阶。小明每…...
MySQL-数据类型
数据类型简介数据表由多列字段构成,每一个字段指定了不同的数据类型,指定了数据类型之后,也就决定了向字段插入的数据内容。不同的数据类型也决定了 MySQL 在存储它们的时候使用的方式,以及在使用它们的时候选择什么运算符号进行运…...
剑指 Offer 32 - II. 从上到下打印二叉树 II(java解题)
剑指 Offer 32 - II. 从上到下打印二叉树 II(java解题)1. 题目2. 解题思路3. 数据类型功能函数总结4. java代码5. 踩坑记录1. 题目 从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。 例如: 给定二叉…...
C#网络爬虫开发
1前言爬虫一般都是用Python来写,生态丰富,动态语言开发速度快,调试也很方便但是我要说但是,动态语言也有其局限性,笔者作为老爬虫带师,几乎各种语言都搞过,现在这个任务并不复杂,用我…...
Fastjson 总结
0x00 前言 这一篇主要是针对已经完成的fastjson系列做一个知识点总结,一来是为了更加有条理的梳理已经存在的内容,二来是为了更好的复习和利用。 0x01 Fastjson基础知识点 1.常见问题: 问:fastjson的触发点是什么?…...
文件路径模块os.path
文件路径模块os.path 文章目录文件路径模块os.path1.概述2.解析路径2.1.拆分路径和文件名split2.2.获取文件名称basename2.3.返回路径第一部分dirname2.4.扩展名称解析路径splitext2.5.返回公共前缀路径commonprefix3.创建路径3.1.拼接路径join3.2.获取家目录3.3.规范化路径nor…...
Kerberos简单介绍及使用
Kerberos作用 简单来说安全相关一般涉及以下方面:用户认证(Kerberos的作用)、用户授权、用户管理.。而Kerberos功能是用户认证,通俗来说解决了证明A是A 的问题。 认证过程(时序图) 核心角色/概念 KDC&…...
DOM编程-全选、全不选和反选
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>全选、全不选和反选</title> </head> <body bgcolor"antiquewhite"> <script type"text/jav…...
C++11可变模板参数
C11可变模板参数一、简介二、语法三、可变模版参数函数3.1、递归函数方式展开参数包3.2、逗号表达式展开参数包一、简介 C11的新特性–可变模版参数(variadic templates)是C11新增的最强大的特性之一,它对参数进行了高度泛化,它能…...
Linux多线程
目录 一、认识线程 1.1 线程概念 1.2 页表 1.3 线程的优缺点 1.3.1 优点 1.3.2 缺点 1.4 线程异常 二、进程 VS 线程 三、Linux线程控制 3.1 POSIX线程库 3.1 线程创建 3.3 线程等待 3.4 线程终止 3.4.1 return退出 3.4.2 pthread_exit() 3.4.3 pthread_cancel…...
Webpack5 环境下 Openlayers 标注(Icon) require 引入图片问题
Webpack5 环境下 Openlayers 标注(Icon) require 引入图片问题环境版本Openlayers 使用 require 问题Webpack5 正确配置构建新环境的时候,偶然发现 Openlayers 使用 require 的方式加载图片(Icon)报错,开始…...
Zookeeper安装部署
文章目录Zookeeper安装部署Zookeeper安装部署 将Zookeeper安装包解压缩, [rootlocalhost opt]# ll 总用量 14032 -rw-r--r--. 1 root root 12392394 10月 13 11:44 apache-zookeeper-3.6.0-bin.tar.gz drwxrwxr-x. 6 root root 4096 10月 18 01:44 redis-5.0.4 …...
Cow Acrobats ( 临项交换贪心 )
题目大意: N 头牛 , 每头牛有一个重量(Weight)和一个力量(Strenth) , N头牛进行排列 , 第 i 头牛的风险值为其上所有牛总重减去自身力量 , 问如何排列可以使最大风险值最小 , 求出这个最小的最大风险值&am…...
MySQL:为什么说应该优先选择普通索引,尽量避免使用唯一索引
前言 在使用MySQL的过程中,随着表数据的逐渐增多,为了更快的查询我们需要的数据,我们会在表中建立不同类型的索引。 今天我们来聊一聊,普通索引和唯一索引的使用场景, 以及为什么说推荐大家优先使用普通索引…...
Spring Cloud alibaba之Feign
JAVA项目中如何实现接口调用?HttpclientHttpclient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持Http协议的客户端编程工具包,并且它支持HTTP协议最新版本和建议。HttpClient相比传统JDK自带的URL Connection&a…...
零信任-Google谷歌零信任介绍(3)
谷歌零信任的介绍? "Zero Trust" 是一种网络安全模型,旨在通过降低网络中的信任级别来防止安全威胁。在零信任模型中,不论请求来自内部网络还是外部网络,系统都将对所有请求进行详细的验证和审核。这意味着每次请求都需…...
Numpy基础——人工智能基础
文章目录一、Numpy概述1.优势2.numpy历史3.Numpy的核心:多维数组4.numpy基础4.1 ndarray数组4.2 内存中的ndarray对象一、Numpy概述 1.优势 Numpy(Nummerical Python),补充了Python语言所欠缺的数值计算能力;Numpy是其它数据分析及机器学习库的底层库&…...
电商仓储与配送云仓是什么?
仓库是整个供给链的关键局部。它们是产品暂停和触摸的点,耗费空间和时间(工时)。空间和时间反过来也是费用。经过开发数学和计算机模型来微调仓库的规划和操作,经理能够显著降低与产品分销相关的劳动力本钱,进步仓库空间应用率,并…...
【零基础入门前端系列】—HTML介绍(一)
【零基础入门前端系列】—HTML介绍(一) 一、什么是HTML HTML是用来描述网页的一种语言HTML指的是超文本标记语言:HyperText Markup LanguageHTML不是一种编程语言,而是一种超文本标记语言,标记语言是一套标记标签(ma…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
