【Linux】进程(7):地址空间

大家好,我是苏貝,本篇博客带大家了解Linux进程(7):地址空间,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


目录

  • (A) 直接看代码,看现象
  • (B)基本理解
  • (C)细节
    • 1. 如何理解地址空间
      • a.什么是划分区域
    • 2. 为什么要有地址空间
      • a.将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域
      • b.进程管理模块和内存管理模块进行解耦
      • c.拦截非法请求
    • 3. 进一步理解页表
    • 4. 进一步理解写时拷贝

(A) 直接看代码,看现象

修改.c文件
在这里插入图片描述

运行进程
在这里插入图片描述

先看黄色框,这个我们能理解,因为之前说过父子进程的数据,如果任意一个都不对数据写入,那么数据就是共享的,所以它们指向同一块数据空间,所以地址相同

再看红色框,我们也能理解。因为进程具有独立性,所以子进程对数据的修改,父进程是看不到的,所以它们打印同一个全局变量,但值不同

再看蓝色框,这就不能理解了,同一个地址的值怎么可能既是100也是300?所以这个地址绝对不可能是物理地址,是虚拟地址。引出下一个概念:虚拟地址

(B)基本理解

当我们将程序加载进内存中时,OS在物理内存中一定要给这个程序开辟一块空间,来保存进程的代码和数据。OS要创建进程的task_struct。我们之前只说task_struct能指向内存中的代码和数据,事实远比我们想的复杂
在这里插入图片描述

在OS内部还要创建地址空间(每个进程都有独立的地址空间)。地址空间在32位机器和64位机器上的大小是不同的。我们以32位机器为例(下面讲的都是在32位机器下),地址空间从低到高一共有4GB的空间。我们之前用C语言打印的地址都是地址空间范围内所对应的地址,而非物理地址

进程的PCB会指向地址空间。

进程的代码和数据都在物理内存里面,进程想访问数据时,数据并不在地址空间上保存,地址空间会给我们提供线性的连续的地址(即虚拟地址),让我们未来提供虚拟地址找到物理地址

在这里插入图片描述

如何通过虚拟地址找到物理地址呢?
在计算机体系结构中还存在页表(每个进程都有自己独立的页表)。页表主要负责将地址空间的虚拟地址和对应的物理地址之间建立映射关系。只要建立好了映射关系,未来上层使用虚拟地址访问时,OS会自动拿着虚拟地址查页表转换成物理地址,最后访问到数据

在这里插入图片描述

现在有进程中有全局变量g_val,&g_val得到的0x601054是虚拟地址,假如g_val的物理地址是0x11223344,那么在页表中就会存储这些地址
在这里插入图片描述

现在进程创建了一个子进程,OS也会为子进程创建task_struct、地址空间和页表(每个进程都有自己独立的地址空间和页表)。
子进程没有代码和数据,它会继承父进程的代码和很多属性,相当于父进程pcb里的很多属性就可以用来初始化子进程。所以子进程的task_struct除了pid,ppid等,大部分属性都和父进程的一样。
子进程的地址空间和页表都是直接拷贝父进程的,所以子进程的地址空间里也有g_val的虚拟地址0x601054,子进程的页表里也有g_val的物理地址
在这里插入图片描述

现在让子进程对数据进行写入:将g_val的值从100改为300。
由于进程具有独立性,所以子进程对g_val的修改,不能影响父进程,所以肯定不能在0x11223344对应的空间修改。OS在写入时,发现g_val不仅被子进程在使用,还同时被父进程使用,所以写入暂停,OS在物理内存中重新开辟一块空间,假设物理地址为0x22334455,然后将g_val的值100拷贝到新空间中。再用新的物理地址覆盖页表中老的物理地址,重新构建映射。
上面的工作(叫写时拷贝)做完,OS再继续执行写入操作。将新的空间的值改为300

在这里插入图片描述

至此,子进程修改g_val的值,只是修改了物理内存和页表,可是上层用到的虚拟地址依旧是0x601054,虽然虚拟地址相同,但被映射到物理内存的不同的区域,所以出现了我们在(A)直接看代码,看现象里地址一样,但值不同的情况

(C)细节

为什么要写时拷贝?
我们上面说了是为了保证进程的独立性。那为什么不在创建子进程的时候,就把数据全部给子进程拷贝?因为如果有数据是父子进程都不需要修改的话,那将这些数据也给子进程拷贝一份,这不就是在浪费空间吗?所以,写时拷贝的本质就是按需申请

1. 如何理解地址空间

a.什么是划分区域

在小学的时候,大家都应该和同桌在桌子上划过“三八线”吧,现在假设你和同桌2个人共用一个100cm的桌子, 你们每个人50cm,那这如何用计算机语言来描述呢?
在这里插入图片描述

只需要构建2个结构体,第二个结构体表示一个课桌分为左右两部分,第一个结构体表示每部分的开始和结束位置,再构建第二个结构体的结构体变量,最后将左右两块空间的起始和终止位置都赋值即可

如果同桌太过分了,每次都侵占了属于你的10cm区域,再用计算机语言来描述

在这里插入图片描述

事实上,地址空间本质是内核的一个struct结构体(struct mm_struct),内核的很多属性都是表示start和end的范围。如何证明呢?

让我们来查看Linux的源代码,我们看到有许多表示开始和结束的变量

在这里插入图片描述

2. 为什么要有地址空间

a.将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域

如果没有地址空间,那么进程的task_struct就要能指向物理内存中对应的所有的数据和代码,这对进程来讲是比较困难的

在这里插入图片描述

现在有了地址空间和页表。实际的物理内存中,代码区、数据区、堆区……都是无序的,如果让进程的task_struct直接指向物理内存的对应的各种代码和数据区,那么可能从低地址往高地址,第一个是初始化数据区,第二个是堆区……就不像有了地址空间和页表,在进程的task_struct视角,从低地址往高地址一定是代码区、初始化数据区……
在这里插入图片描述

所以地址空间的第一个好处就是将无序变有序(对task_struct),让进程以统一的视角看待自己运行的各个区域

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

比如现在进程要申请一段堆空间,那先在地址空间的堆区申请一段空间,但是进程不是立马就要用,所以暂时不在物理内存申请空间,也不在页表建立映射关系(只有虚拟地址,没有物理地址),等到进程需要用这段空间,再在物理内存申请空间并建立映射关系

换言之,如果要对进程做各种管理,那么内存管理都可以延迟处理,因此地址空间和页表的存在,能让进程管理模块和内存管理模块解耦

c.拦截非法请求

当我们写的代码在遍历时要访问地址空间的堆区(我们在上层使用的都是虚拟地址),但发生了越界,此时将这个越界的虚拟地址到页表中查,发现没有这个虚拟地址,证明没有对应的映射关系,所以OS就拦截了这次请求,不让它做任何操作,就不会有往物理内存中写入的操作,所以能拦截非法请求

3. 进一步理解页表

CPU内的寄存器(如CR3)能将当前页表的地址保存在CPU内
MMU(硬件):将虚拟地址结合页表转换成物理地址
在这里插入图片描述

页表中有许多标记位,比如用来确定当前物理地址指向的空间是否在内存中,是:标记位为1;否:标记位为0

什么情况下,当前物理地址指向的空间不在内存中呢?
进程挂起。如果操作系统内存特别吃紧,且进程处于阻塞态,那么操作系统会将内存中的代码和数据加载到磁盘的swap分区,那么此时物理地址指向的空间就不在内存中,该标记位就为0

页表中还有rwx权限标记位。我们以前见过下面的代码,你觉得这个代码能被VS运行通过吗?
在这里插入图片描述

显然不能,在语言层面上讲,因为”hello world”是常量字符串,位于字符常量区,所以不能被修改
在操作系统层面,就是字符常量区的虚拟地址在页表上的rwx权限标记位为r,没有w,所以在想将“web”写入时,系统检测到了错误,在转化成物理地址中将进程终止,所以根本没有写入物理内存

4. 进一步理解写时拷贝

在进程没有创建子进程时,全局变量g_val的虚拟地址被记录在页表中,对应的权限标记位为rw,可读可写

在这里插入图片描述

进程创建子进程后,全局变量g_val的虚拟地址被记录在页表中,对应的权限标记位被OS设为r。子进程会拷贝父进程的地址空间和页表,所以子进程的g_val在页表对应权限标记位也是r

在这里插入图片描述

当父子进程任意一个想对g_val进行写入时,将g_val的虚拟地址到页表中查,发现其权限标记位为r,OS识别到错误,开始判断

  1. 是不是数据不在物理内存(进程挂起了,页表中对应标记位为0):如果是,触发缺页中断(让OS重新在物理内存中开辟空间,重新建立映射,把标记位置为1,然后再继续访问)。这属于正常情况
  2. 是不是数据需要写时拷贝(OS如何知道需要写时拷贝,这个以后再讲),如果是,就发生写时拷贝
  3. 上面2种都不是,进行异常处理

好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️

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

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

相关文章

go语言接口之sort.Interface接口

排序操作和字符串格式化一样是很多程序经常使用的操作。尽管一个最短的快排程序只要15 行就可以搞定,但是一个健壮的实现需要更多的代码,并且我们不希望每次我们需要的时候 都重写或者拷贝这些代码。 幸运的是,sort包内置的提供了根据一些排序…

PostgreSQL和MySQL架构模型的区别

PostgreSQL - 多进程架构 PostgreSQL采用了一种多进程架构。在这种模型下,每个数据库连接被分配给一个新的服务器进程(或者说是数据库进程)。这种方式为每个连接提供了独立的内存空间和执行环境。这样做的优点是提高了系统的稳定性和隔离性&…

法国人工智能初创公司 Mistral 正在推出新的人工智能模型定制选项服务和 SDK

Mistral AI是一家成立于2023年的法国人工智能初创公司,由Artur Mensch、Timothe Lacroix和Guillaume Lample三位前Meta和Google DeepMind的研究人员创立。该公司专注于生成式AI技术,特别是用于构建在线聊天机器人、搜索引擎等应用。 Mistral AI在成立之…

python之List记录

1. 列表(List)基础 Python中的列表是一种可变、有序的元素集合,元素之间通过逗号分隔并包含在一对方括号内。列表的元素可以是不同类型的数据。 创建列表 # 创建一个包含不同类型元素的列表 my_list [1, 2.5, hello, True, [1, 2, 3]]访问…

[数据集][图像分类]城市异常情况路边倒树火灾水灾交通事故分类数据集15223张8类别

数据集类型:图像分类用,不可用于目标检测无标注文件 数据集格式:仅仅包含jpg图片,每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数):15223 分类类别数:8 类别名称:[“badroad”,“fallentree”,“f…

Android-Q升级-Camera记录

目录 代码环境 建立Android Q使用的camera仓 Camera底层适配 camx 原生接口变化 其他编译问题 chi-cdk 数据类型不匹配 case未加break的报错 libalRnBRT_GL_GBWRAPPER链接问题 vidhance编译错误 libarcsat链接问题 vendor/qcom/proprietary prebuilt_HY11 调试cam…

floor函数

添加链接描述\ #include<bits/stdc.h>using namespace std;const int N 10;int main() {int n;cin>>n;for(int i1;i<50000;i){if(floor(i*1.08)n){cout<<i;return 0;}}cout<<":(";return 0; }floor函数是向下取整 ceil是向上取整 round…

CarSim车辆运动轨迹绘制

CarSim车辆运动轨迹绘制 CarSim中与车辆位置有关的信息分别为Xo和Yo 输出到Simulink中 导入到工作空间中保存&#xff0c;low_carsim_path.mat &#xff0c;绘制结果曲线&#xff0c;low_carsim_path_comp.m data csvread(low_two_path.csv,1,0); low_two_path_x data(:,1)…

【CentOS】手动编译安装make、cmake、gcc、git

摘要 Centos7升级make和gcc版本到最新——CSDN make make 各个版本下载地址 http://ftp.gnu.org/pub/gnu/make 以4.4为例安装&#xff1a; # 下载 wget https://ftp.gnu.org/pub/gnu/make/make-4.4.tar.gz # 解压配置 tar zxf make-4.4.tar.gz cd make-4.4 ./configure --p…

分享我的新版FMEA培训心得

近日&#xff0c;我有幸参加了深圳天行健企业管理咨询公司举办的新版FMEA培训&#xff0c;这次学习不仅让我对FMEA有了更深入的理解&#xff0c;更使我在实际工作中找到了提升产品质量的新路径。 新版FMEA相较于传统版本&#xff0c;更加注重风险识别与预防&#xff0c;强调在…

Java 开发面试题精选:分布式锁相关一篇全搞定

面试路上&#xff0c;分布式锁始终是绕不开的坎&#xff1f;别怕&#xff0c;这篇精心准备的文章正是您的通关秘籍&#xff01;这篇文章聚焦面试官最青睐的提问点&#xff1a;从分布式锁基础概念到其实现机理&#xff0c;再到它在多场景下的应用智慧&#xff1b;深入剖析性能优…

C# FFmpeg 音视频开发总结

&#x1f3c6;作者&#xff1a;科技、互联网行业优质创作者 &#x1f3c6;专注领域&#xff1a;.Net技术、软件架构、人工智能、数字化转型、DeveloperSharp、微服务、工业互联网、智能制造 &#x1f3c6;欢迎关注我&#xff08;Net数字智慧化基地&#xff09;&#xff0c;里面…

golang通道(chan)选择(select)与关闭(close)使用示例

1.通道选择 创建两个双向通道 c1 : make(chan string) //双向通道 c2 : make(chan string) //双向通道 向通道写入数据 //协程1向通道1写数据go func() { c1 <- "hello world from c1" }()//协程2向通道2写数据go func() { c2 <- "hello world from …

【递归、搜索与回溯】递归算法

一、经验总结 递归 VS 迭代&#xff08;循环&#xff09; 递归和迭代都解决的是重复的子问题&#xff0c;因此两者是可以相互转化的。利用栈结构可以将递归算法转化为迭代算法。 递归和迭代各有其优缺点&#xff0c;选择时需根据具体场景和需求来决定。 递归的优点包括&#…

苹果眼镜(Vision Pro)专业咨询服务模式优化方案

一、精准定位&#xff1a; 专注于为Apple Vision Pro应用开发者提供一站式、全方位的专业咨询服务&#xff0c;致力于成为开发者在空间计算时代中不可或缺的合作伙伴&#xff0c;共同打造“下一个大事件”。 二、核心业务优化&#xff1a; visionOS策略咨询&#xff1a; 深入…

【氵】Archlinux+KDE Plasma+Wayland 安装nvidia驱动 / 开启HDR

参考: NVIDIA - Arch Linux 中文维基 &#xff08;其实就是把 wiki 简化了一下 注&#xff1a;本教程适用 GeForce 930 起、10 系至 20 系、 Quadro / Tesla / Tegra K-系列以及更新的显卡&#xff08;NV110 以及更新的显卡家族&#xff09;&#xff0c;此处以 RTX3060 为例 …

LlamIndex二 RAG应用开发

在AutoGen)系列后&#xff0c;我又开始了LlamIndex 系列。欢迎查询LlamaIndex 一 简单文档查询 - 掘金 (juejin.cn)了解LlamIndex&#xff0c;今天我们来看看LlamIndex的拿手戏&#xff0c;RAG应用开发。 何为RAG&#xff1f; RAG全称"Retrieval-Augmented Generation&q…

MySQL之创建高性能的索引和查询性能优化(一)

创建高性能的索引 减少索引和数据的碎片 B-Tree索引可能会碎片化&#xff0c;这会降低查询的效率。碎片化的索引可能会以很差或者无序的方式存储在磁盘上。根据设计&#xff0c;B-Tree需要随机磁盘访问才能定位到叶子页&#xff0c;所以随机访问是不可避免地。然而&#xff0…

大田作业全覆盖算法

概述 大田作业全覆盖算法是指在农业生产中&#xff0c;通过合理的路径规划和作业安排&#xff0c;实现对田间作业的全覆盖&#xff0c;避免遗漏任何区域&#xff0c;提高作业效率和质量。该算法通常用于农业机械&#xff0c;如拖拉机、无人机、自动化农机等。以下是实现大田作…

vue处理json数据

背景&#xff1a;后端返回的数据不是我想要的&#xff0c;现在需要把 name 替换为title&#xff08;小声蛐蛐&#xff1a;又让我处理数据&#xff09; 后端返回数据格式 修改字段操作&#xff1a;&#xff08;使用递归遍历的方式将title属性赋了name的值&#xff09; renderT…