【进程概念④】:进程地址空间(虚拟内存与物理内存)
【进程概念④】:进程地址空间(虚拟内存与物理内存)
- 一.进程地址空间
- 二.分页与虚拟地址
- ①.what
- ②.how
- ③.why
- 三.页表细节
- ①.标志位
- ②.缺页中断
- 四.总结意义
一.进程地址空间
你觉得我们代码中写的数据都在哪存储着呢?
在内存里存着!确实是在内存里存储着,那你知道数据存在内存的哪里呢?
你以前肯定听过栈,堆,静态区等等的。都是在"内存"上划分的。

比如局部变量是在栈上开辟的,动态申请的空间是在堆区申请的,全局变量是在全局区(这里又分成初始化数据和未初始化数据)代码都是存在代码段的。
对于栈呢,它是向下增长,也就是向地址减少的地方增长的;对于堆呢,它是向上增长,也就是向地址增长的方向增长的。
所以在我们没有学习进程地址空间时,我们对于内存的印象就是如上图所示。而学习进程地址空间后,你就会发现其实这个并不是真正的"内存"。
理解进程地址空间,我们先理解一下下面这个问题:
【问题】我们知道fork()创建进程时会返回两个值,为什么同一个变量可以接受两不同的值呢?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{pid_t id = fork();if(id < 0){perror("fork");return 0;}else if(id == 0){ //child子进程g_val=100;//将全局变量改成100;printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{ //parent父进程printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}

我们在全局定义一个变量g_val.并初始化为0.
然后利用fork()函数创建两个进程,一个为子进程,一个为父进程,子进程先执行,在子进程中将全局变量修改成g_val修改成100.并打印它的值和地址。在父进程中修改全局变量的值,打印它的值和地址。最后结果显示,子进程和父进程中的全局变量值并不一样,而地址确实一样的,这是为什么呢?
我们可以猜想:
1.变量内容不相同,父子进程中输入的变量肯定不是同一个变量。
2.但是父子进程中,变量的地址却又相同,这说明这个地址肯定不是物理地址,因为物理地址只能一对一,不存在一对多的情况。
其实这里的地址是虚拟地址,在Linux中,我们用c/C++语言所看到的地址都是虚拟地址,都不是物理地址,物理地址用户是看不到的。
二.分页与虚拟地址
其实呢进程并不是直接与物理内存进行交互的,中间还存在着一个叫做进程地址空间的对象。进程地址空间其实就是虚拟内存,而物理内存才是真正的内存,除了进程地址空间外,中间还存在一个叫页表的对象,它是用来将虚拟地址转换映射成物理地址的。页表中其实有很多标识位,比如权限位和判断位等等。最主要还是它可以将虚拟地址映射到物理地址上去,页表的左边存的都是虚拟地址,页表的右边则是存着物理地址。这就形成了映射。当进程访问内存时,先访问的是虚拟内存,然后才是根据页表映射转换到物理内存。

当使用fork()创建子进程时,子进程也需要进程地址空间,而子进程的进程地址空间是继承父进程的,也就是直接从父进程那拷贝过来的。
子进程的页表其实也是从父进程那拷贝过来的,所以父子进程默认情况下,数据和代码指向都是一样的,也就印证了父子代码共享,数据一般也是共享的,除非发生写实拷贝。

好的,我们现在再转回到问题上去:为什么fork()返回的两个值可以存在同一个变量里?为什么父子进程的变量值不相同,但地址却相同呢?
首先我们要确定的是,他们这里面发生了写入数据导致发生写实拷贝了。fork()返回返回值时是不是写入?改变全局变量的值是不是写入?
那么写时拷贝发生了什么呢?
写时拷贝,父子进程先写入的就会将要访问的数据拷贝一份,重新开辟内存创建变量。所以关键在于重新开辟了一块空间了。
这时子进程的物理地址就不再指向父进程原来的变量位置,而是指向一块新开辟的空间了。但是这个写时拷贝的过程,并不影响进程虚拟地址。
所以我们就可以知道为什么父子进程中同一个变量值不同了,是因为变量的物理内存不同,它们的地址却相同,是因为写时拷贝不影响虚拟内存。

①.what
什么是进程地址空间呢?

什么是32位机器呢?就是它有32个总线。每一根地址总线只有0,1表示。那么可以表示的范围就是0–2^32种了。而这就是该机器下所能表示的内存范围,至于地址空间是什么,就是地址总线排列组合形成的地址范围,能访问的最大范围,也就是一段数据(地址)的范围。
②.how
如何理解地址空间上的区域划分呢?区域又是如何进行划分的呢?

我们可以假设有一块连续空间,被小胖和小芳两个人共同使用,但是小胖总是喜欢跑到小芳的位置上吃零食,睡觉,小芳这能忍吗?这当然不能忍了。所以小芳就提出划分区域,定出一个三八线出来,不给小胖逾越。那么你想一想应该如何帮助小芳进行区域的划分呢?
我们可以直接定义一个桌面区域结构体,它里面包含着四个变量,小胖的起始位置和小胖的终点位置,小芳的起始位置和小芳的终点位置。这里通过这个结构体对象,我们就可以对该区域进行划分,小芳想要公平公正一些就各自一人一般的区间,谁也不能逾越。
所以结构体对象就可以这样定义:
struct desk_area justic{ 1,50,51,100 };
所以对于每一个进程来说,除了要有PCB外,还需要进程地址空间。
在进程创建时,操作系统处理创建一个PCB对象给进程,还要创建一个进程地址空间对象给进程。并且操作系统还要对这个地址空间进行管理,至于如何管理呢?先描述,再组织!

操作系统会像上面一样首先会对进程地址空间进行描述,将空间区域的划分边界变量确定下来。操作系统不是直接在PCB里创建进程地址空间对象,而是在PCB里存储一个指向进程地址空间的指针,然后再利用数据结构进行管理。
1.其实所谓的进程地址空间,本质上就是一个描述进程可视范围的大小,就是进程可以看到内存的范围。
2.地址空间里会存在区域的划分,划分的手段,就是将线性地址发个成不同区的start和end。
3.进程空间本质也是内核的一个数据结构对象,类似于PCB。地址空间也是需要被操作系统管理的。

在进程PCB里存着一个指针,叫做struct mm_struct的对象指针。这个对象指针就是指向进程地址空间的。

③.why
为什么要有进程地址空间呢?
在这里我们对进程的理解更深入了,进程的概念仍然是:内核数据结构+自己写的代码和数据。只不过这里的内核数据结构
除了PCB对象外,还有进程地址空间对象。
在没有进程地址空间以前,进程都是直接访问物理地址的,这很危险,进程管理和内存管理两个直接出现了强耦合。当内存管理出现了问题,进程就挂了,比如如果是越界访问,进程就直接挂掉,但就怕有人将物理地址空间修改了,内容改变了,这可能会影响其他进程。

而现在呢,有了进程空间地址,我们不再直接跟物理内存进行访问,而是通过虚拟内存进行访问,如果出现了问题,虚拟内存的机制会进行拦截除了的,并不会到达物理内存,这样就大大的保护了物理内存了。
1.所以进程地址空间可以让我访问内存时,增加一个转换的过程,在这个转换的过程中,可以对我们的寻址请求进行审查,一旦出现异常访问,直接拦截,该请求不会到达物理内存,保护物理内存。
2.可以让进程一统一的视角看待内部。因为物理内存是没有区域划分的,是可以随意任何地方开辟空间的,而虚拟内存则是划分区域的;最后可以让无序变有序。
3.减少了强耦合,一旦内存管理出现问题,会影响其他进程。
三.页表细节
①.标志位
页表是在哪呢?在CPU的一个叫cr3的寄存器里,里面存储着页表的起始地址。
我们要理解当进行需要使用页表时,肯定表面该进程正在运行使用中,那么肯定在CPU上运行着,所以进程直接通过CPU上的寄存器就可以找到该进程的页表。
在深入理解页表之前,我们先理解一个问题
【问题】为什么只读区的数据只读不给写的?静态成员为什么不能修改呢?代码是只读的?为什么呢?
对于物理内存来说,没有只读概念,都是可访问的,所以实现这个操作的关键在页表。
在页表中除了虚拟内存映射物理内存外,还有一个标识位。
表示该内存是否可以读写,r表示只读,rw表示可读可写。
所以对于代码区域的或者静态区的虚拟内存确实映射到物理内存上了,但页表还给它进行了标识r表示只读。这样该数据就只能是只读状态不可写。

②.缺页中断
我们知道进程挂起时,会将进程的代码和数据先放入磁盘中省出空间,给CPU使用,那么我们如何确定进程的代码和数据被放入磁盘里了呢?

对于虚拟内存,地址都可以先将加载到页表里,而物理内存可以先不加载进去,然后页表里存在一个标志位,用来表示该物理内存是否存在,0表示不存在,1表示存在。
所以当CPU需要访问某个数据时,首先会根据页表从虚拟地址转换到物理地址,如果发现页表里没有加载该数据的物理地址,页表里的标识位也为0.操作系统会给这个数据申请内存,然后将申请的内存地址加载到页表里,并将标识位改成1,然后CPU再重新访问页表映射到物理地址。这时就可以正常访问数据了,这个过程叫做页表中断。

所以对于进程来说,一开始没有数据加载到内存里,也是可以创建PCB内核数据结构的,等当真正访问数据时再将数据加载进来。
并且就算程序运行起来了,数据加载进来了,也不代表所有的数据都加载进来。
写时拷贝的本质也就是发生了缺页中断。
四.总结意义
当发生缺页中断时,内存会重新申请,页表的物理地址会被填充内存的释放等过程跟进程是没有关系的,进程不需要关系这些问题。
进程只需向虚拟内存申请空间,释放空间。如果页表中的物理地址不存在,就会发生缺页中断自动调用内存管理的功能,去向内存中申请空间。
所有进程地址空间和页表的存在,使得进程管理根本不需要关心内存管理。

1.总结进程地址空间意义:
2.什么是统一的视角看待内存呢?
3.并且我们可以从更深层去阐明进程之间为什么存在着独立性:虚拟地址可以完全一样,但物理地址不同!
4.进程地址空间的存在使得物理内存不再被看见!
相关文章:
【进程概念④】:进程地址空间(虚拟内存与物理内存)
【进程概念④】:进程地址空间(虚拟内存与物理内存) 一.进程地址空间二.分页与虚拟地址①.what②.how③.why 三.页表细节①.标志位②.缺页中断 四.总结意义 一.进程地址空间 你觉得我们代码中写的数据都在哪存储着呢? 在内存里存着࿰…...
C语言内存四分区
四个区域:代码区,全局区,栈区,堆区 ①代码区 存放所写代码,二进制内容 ②全局区(又分data区和bss区) 存放全局变量,静态变量,常量 data区:已经初始化的全局变…...
数据可视化报表分享:区域管理驾驶舱
在零售数据分析中,区域管理驾驶舱报表是用来分析企业运营数据,以制定销售策略和提高利润。因此这张报表需要整合大量数据,数据整合、分析、指标计算的工作量极大,在讲究高效率、高度及时性的大数据时代,BI数据可视化分…...
解决pip安装包后但是Pycharm检测不到
首先要知道python找包的原理:原理 之后把一下代码打印一下: import sys print(sys.executable)# /usr/bin/python2 print(sys.path)# [/usr/lib/python2.7, /usr/lib/python2.7/dist-packages, /usr/local/lib/python2.7/dist-packages] print(sys.prefi…...
折纸问题
折纸的次数 —— 从上到下的折痕 本质上是中序遍历的问题,因为每一次在已有的折痕后折的时候,当前折痕上的折痕一定为凹,当前折痕下的折痕一定为凸 。实际模拟了一个不存在的二叉树结构的中序遍历。 注:折纸折几次整颗二叉树就有…...
mysql-面试50题-2
一、查询数据 学生表 Student create table Student(SId varchar(10),Sname varchar(10),Sage datetime,Ssex varchar(10)); insert into Student values(01 , 赵雷 , 1990-01-01 , 男); insert into Student values(02 , 钱电 , 1990-12-21 , 男); insert into Student v…...
FoLR:Focus on Local Regions for Query-based Object Detection论文学习笔记
论文地址:https://arxiv.org/abs/2310.06470 自从DETR问询式检测器首次亮相以来,基于查询的方法在目标检测中引起了广泛关注。然而,这些方法面临着收敛速度慢和性能亚优等挑战。值得注意的是,在目标检测中,自注意力机制…...
【QT开发(15)】QT在没有桌面的系统中可以使用
在没有桌面的系统中,可以使用QT库。QT库可以在没有图形用户界面(GUI)的环境中运行,例如在服务器或命令行终端中。 这样就可利用Qt的: 对象模型,信号和槽容器类多线程和多进程网络编程 等...
『heqingchun-Qt的艺术-优雅界面设计开发』
Qt的艺术-优雅界面设计开发 效果图 一、新建Qt窗口工程 二、准备资源文件 1.图标资源 链接: 图标资源 2.Qss资源 链接: Qss资源 三、设计开发 项目源码链接: CSDN资源...
webGL编程指南 第四章 平移+旋转.RotatdTanslatedTriangle.html
我会持续更新关于wegl的编程指南中的代码。 当前的代码不会使用书中的缩写,每一步都是会展开写。希望能给后来学习的一些帮助 git代码地址 :git 本篇文章将把旋转和平位移结合起来,因为矩阵的不存在交换法则 文章中设计的矩阵地址在这里…...
使用canvas实现时间轴上滑块的各种常用操作(仅供参考)
一、简介 使用canvas,模拟绘制时间轴区域,有时间刻度标尺,时间轴区域上会有多行,每行都有一个滑块。 1、时间刻度标尺可以拖动,会自动对齐整数点秒数,最小步数为0.1秒。 2、滑块可以自由拖动,…...
Netty优化-扩展自定义协议中的序列化算法
Netty优化-扩展自定义协议中的序列化算法 一. 优化与源码1. 优化1.1 扩展自定义协议中的序列化算法 一. 优化与源码 1. 优化 1.1 扩展自定义协议中的序列化算法 序列化,反序列化主要用在消息正文的转换上 序列化时,需要将 Java 对象变为要传输的数据…...
【Java网络编程】二
本文主要介绍了传输层的UDP协议和TCP协议,以及在Java中如何通过Socket套接字实现网络编程(内附UDP和TCP版本的回显服务器代码) 一.网络通信 网络编程,就是写一个应用程序,让这个程序可以使用网络通信,这里就…...
通过IP地址可以做什么
通过IP地址可以做很多事情,因为它是互联网通信的基础之一。本文将探讨IP地址的定义、用途以及一些可能的应用。 IP地址的用途 1. 设备标识:IP地址用于标识互联网上的每个设备,这包括计算机、服务器、路由器、智能手机等。它类似于我们日常生…...
前端 CSS 经典:clip、clip-path
1. clip 1.1 clip: auto | inherit | rect auto:默认,不裁剪 inherit:继承父级 clip 属性 rect:规则四边形裁剪 1.2 clip: rect(top, right, bottom, left) 注意: 1.裁剪只对 fixed 和 absolute 的元素有效。 2.top&…...
android 如何判断已配对的蓝牙是否打开了互联网访问开关
最近遇到一个需求,要判断已配对的蓝牙是否打开了互联网访问的开关。 经查看源码,得出以下方法。 1. 首先要判断蓝牙是否打开 2. 已打开的蓝牙是否已配对 3. 验证是否真正打开 /*** 是否打开蓝牙互联网访问*/SuppressLint("MissingPermission&quo…...
在Linux上实现ECAT主站
在Linux上实现ECAT主站 引言介绍EtherCATSOEM 使用下载ECAT主站编译 引言 EtherCAT由一个主站设备和多个从站设备组成。主站设备使用标准的以太网控制器,具有良好的兼容性,任何具有网络接口卡的计算机和具有以太网控制的嵌入式设备都可以作为EtherCAT的…...
Spring Cloud之服务熔断与降级(Hystrix)
目录 Hystrix 概念 作用 服务降级 简介 使用场景 接口降级 服务端服务降级 1.添加依赖 2.定义接口 3.实现接口 4.Controller类使用 5.启动类添加注释 6.浏览器访问 客户端服务降级 1.添加依赖 2.application.yml 中添加配置 3.定义接口 4.Controller类使用 …...
HashMap 哈希碰撞、负载因子、插入方式、扩容倍数
HashMap 怎么解决的哈希碰撞问题? 主要采用了链地址法。具体来说: 每个哈希桶不仅存储一个键-值对,而是存储一个链表或树结构。这样,具有相同哈希值的键-值对可以被存储在同一个哈希桶中,并通过链表或树结构来解决碰…...
【Unity3D】Unity与Android交互
1 Unity 发布 apk 1.1 安装 Android Build Support 在 Unity Hub 中打开添加模块窗口,操作如下。 选择 Android Build Support 安装,如下(笔者这里已安装过)。 创建一个 Unity 项目,依次点击【File→Build Settings→…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...




