Linux:程序地址空间详解

目录

一、堆、栈、环境参数所在位置

二、进程地址空间底层实现原理

​编辑

三、什么是地址空间

四、为什么要有进程地址空间

五、细谈写实拷贝的实现及意义


e5b3d563b2d74e598930bb9339ca987d.png

在C/C++学习中,都学习过如上图所示的一套存储结构,我们大致知道一般存储空间分为堆区,栈区,静态区等,可我们对其构成和实现原理并不理解,而今天博主将通过Linux基于xshell平台来对其进行深度刨析。

一、堆、栈、环境参数所在位置

5ef52bd098f8440cacc9b022fb60dcfd.png

我们可以通过一段代码来验证上图所划分区域的准确性进行验证:使用makefile编译myenv然后运行

536bd40f6f524931becd80e2e9fab28f.png

78f59c2fdf0c4ac98b14b70c8ff7558f.png

 在Linux环境下,我们可以很清晰的看到不同类型的数据的存储位置,而在VS下却不一定遵守此套规则,因为在Windows环境下,考虑到安全性会对地址进行随机化,而且每次打印出的地址都不一样,为了防止代码和数据被固定编译到某个地方。

二、进程地址空间底层实现原理

6a4eeba7ef99424792a8ac6e765ebad7.png

34c7e36c643d4eb59df1221055529447.png

父子进程内容不一样,地址却一样?从物理层面上来讲这是不可能的,同一块地址却有两个值,此时只能说明,此地址不是物理地址而是虚拟地址。所以我们日常编写代码过程中使用的地址也都是虚拟地址!!

所以上图展示出的所谓的内存分布结构,也不是物理层面的结构,而是由虚拟地址所构建出的结构,而它正确的叫法叫做进程地址空间

通过之前的了解我们可以知道,在进行fork创建子进程时,子进程会继承父进程的代码 数据和属性信息,所以子进程和父进程中都会存在一个名为g_val的一个全局变量,而在子进程对其进行修改之前父子进程中的g_val在进程地址空间中都是同一块空间即他们的虚拟地址都是一样的,而此时每个进程PCB即task_struct中都会有一张表(页表)来将代码中数据的虚拟地址通过该表来映射到真实的物理地址,可以理解为一个数组,g_val的虚拟地址为数组下标,而下标所对应的元素中存的值就是真实的物理地址。

而子进程在创建时也会继承父进程中的这张表,在子进程要对g_val的值进程更改时,遵循进程之间相互独立互不影响的原则,此时就会进行写时拷贝,操作系统会在真实的物理内存中新开辟一块空间然后将原本在哈希表中g_val下标所存的地址所指向的物理内存中的值进行拷贝然后修改为200,然后再将g_val虚拟地址为下标的元素中的值更换为这个新开辟的物理内存的地址,此时就完美实现了父子进程中同一变量却存着不一样的值的效果!

所以父子进程对g_val进行访问时通过不同的映射关系找到不同的物理内存。

所以这也是fork指令后,父子进程明明是同一段代码,却能使用getpid赋值给同一个变量不同值的原因。这也使得我们可以getpid后通过获取到的id值来对父子进程进行分流从而时父子进程实现不同的功能。以此为切入点,我们详细刨析地址空间。

三、什么是地址空间

每一个进程都会存在一个进程地址空间。操作系统要对其进行管理依旧是遵循先描述再组织的原则。

所以进程地址空间的本质是数据结构,具体到进程中,就是特定数据结构的对象。

 53dc3a539c4a40859e9659acc3e1b4e0.png

所以操作系统每创建一个进程就创建一个地址空间,内部用next指针链接起来,所以对进程地址空间的管理就变成了对链表的增删查改。

b127ed3400db44daafa6c50f068aa91e.png

每个进程创建的时候都会存在一个struct task_struct,而每个task_struct中都存在一个指针指向对应的进程地址空间。所以进程和地址空间的关系就是数据结构之间的关系。 

所以在设计进程的地址空间时,如何将地址空间进行区域划分呢?

f3505af53df64be8a4d6560d877b211a.png

直接上源码:

cb28dc2e359d4cc8a0a27870c2ec3676.png

e6717654e3ac4d7fb64df89bd1553985.png

可以很清晰的看到,在linux内核中存在一个struct,里面存放着各个区域在内存中开始和结束的位置 ,即大量的start/end。所以对应的地址空间本质上是进程的一种数据结构。

所以当操作系统创建进程时,下图的数据结构就会被进程创建出来,然后将里面的字段进行初始化。所以每个进程都有自己的mm_struct.。里面有着该进程的各种信息。

e5b3d563b2d74e598930bb9339ca987d.png

而空间划分本质就是区域内的各个地址都可以使用。而结构体中的地址空间是虚拟的,本身并不具有代码和数据保存能力,代码和数据必须存放在物理内存中,所以就必须将虚拟地址(线性地址)转化成为物理内存中,这时就需要用到我们上文中所说的那张表来进行映射,这张表就叫做页表。

048cd30d97b148899c1b8200fe2e31e0.png

一个进程在启动时,先创建PCB再创建mm_struct即进程地址空间,然后将磁盘中的可执行程序(代码和数据)加载到物理内存当。随后mm_struct中的正文代码数据等都会通过页表转换到物理地址。

 而完成页表映射,并去页表进行查找的工作都是由CPU完成的,在CPU中有个mmu集成硬件专门用来转换工作,其中有一个名为CR3的寄存器,寄存器指向页表的起始位置,当CPU执行代码需要去内存中查找变量或者存储变量时,mmu会通过CR3会去页表中进行查找,然后找到真实物理地址,注意:CR3中保存的地址就是页表的物理地址,因为虚拟地址需要CR3来进行页表查找,它内部存的如果是页表的虚拟地址,就无法转化出页表的物理地址,经典的蛋生鸡,鸡生蛋的问题。

虚拟地址是给进程的/给用户的!

经过页表的映射,就成功的将进程的管理和内存的管理成功的分离开,这样操作系统在对进程进行管理时,就直接给予虚拟地址所构建出的上图的数据结构进行管理就可以,而对物理内存,也不需要担心存储不连续导致的一系列问题,即使一串代码分开存储,在页表中页可以通过映射来将它们放到一起映射到同一虚拟地址中。

再回头看父子进程创建的整个过程就显得透明清晰了。

四、为什么要有进程地址空间

1.将物理内存从无序变有序,让进程以统一的视角,看待内存

2.将进程和内存管理进行解耦合

3地址空间+页表是保护内存安全的重要手段,如果访问内存的请求合理就通过页表去访问物理内存,如果访问请求非法就及时拦截。

所以在日常我们越界访问或者出现野指针时,就会出现报错,但这种非法访问并不会导致操作系统和程序崩溃,因为拦截此此次访问操作的是地址空间。以此保护了内存。

上面的图就足矣说名问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!

 

4.地址空间的存在可以优化内存申请分配的方式

操作系统,一定要为效率和资源使用率负责。

比如我们在new或者malloc的时候,操作系统并不会直接去物理内存开辟新的空间,而是在进程的虚拟地址空间中申请,所以,申请内存的本质是在地址空间中申请

因为申请空间并不代表要马上使用空间,当空间申请时,操作系统先在虚拟地址空间中分配对应大小的空间,当要实际去进行存储和使用时,再去通过页表建立映射关系在物理内存中开辟空间。开辟虚拟地址空间时去建立映射和使用时再去建立映射本质上没有太大差别,使用时再去建立映射反而有以下两点好处:

1、充分保证内存的使用率

2、提升new或malloc的速度

五、细谈写实拷贝的实现及意义

 同样遵循上述规律,子进程在刚开始被创建时,虽然有自己的页表mm_struct和pcb,但在物理内存上并没有创建新的内存空间来拷贝父进程的数据而是和父进程指向相同的空间,当子进程要去修改数据时再进行写实拷贝。

而写实拷贝的应用场景不止是对数据进行更改,还有可能进行增删查等工作。

而页表也并不只有虚拟地址和物理地址两个部分,还有一个权限位,所以对以上代码对str进行更改时就需要去页表进行映射,而“hello linux”作为常量字符串具有常性,不能被修改,而编译器是怎么知道不能修改的呢?不加const的问题也存在与此,加了const以后编译时直接会在编译时报错,而不加是在运行时报错,加了const相当于将运行时的报错进行提前,而运行时的报错藏的是比较深的有时甚至不会被发现。所以加cosnt属于防御性编程。

所以根据示例可以知道,页表也是有权限的,当加const时权限位就变成只读权限。

所以如上图,我们fork创建子进程时,默认的代码段和数据段都是r只读权限,所以在读的时候操作系统就不会触发任何错误,直接通过页表来对内存进行读取,而当我们尝试修改时,因为此时页表中的权限位是r权限,此时就会引发操作系统来进行处理,而我们也将此情况称为缺页中断。

此时引发缺页中断时,操作系统作为管理者就会进行写实拷贝。

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

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

相关文章

代码随想录算法训练营三刷 day38 | 动态规划之 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

三刷day38 509. 斐波那契数1 确定dp数组以及下标的含义2 确定递推公式3 dp数组如何初始化4 确定遍历顺序5 举例推导dp数组 70. 爬楼梯1 确定dp数组以及下标的含义2 确定递推公式3 dp数组如何初始化4 确定遍历顺序5 举例推导dp数组 746. 使用最小花费爬楼梯1 确定dp数组以及下标…

[C++初阶] 爱上C++ : 与C++的第一次约会

🔥个人主页:guoguoqiang 🔥专栏:我与C的爱恋 本篇内容带大家浅浅的了解一下C中的命名空间。 在c中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称…

C++进阶,手把手带你学继承

🪐🪐🪐欢迎来到程序员餐厅💫💫💫 主厨:邪王真眼 主厨的主页:Chef‘s blog 所属专栏:c大冒险 总有光环在陨落,总有新星在闪烁 【本节目标】 1.继…

【讲解下go和java的区别】

🔥博主:程序员不想YY啊🔥 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家💫 🤗点赞🎈收藏⭐再看💫养成习惯 🌈希望本文对您有所裨益,如有…

Windows安装tomcat,以服务的方式管理,如何设置虚拟内存

之前工作中,部署tomcat都是使用Linux服务器,最近遇到个客户,提供的服务器是Windows server,并且需要通过服务的方式管理tomcat;以自己多年的码农经验,感觉应该没有问题,结果啪啪打脸了&#xf…

python安装删除以及pip的使用

目录 你无法想象新手到底会在什么地方出问题——十二个小时的血泪之言! 问题引入 python modify setup 隐藏文件夹 环境变量的配置 彻底删除python 其他零碎发现 管理员终端 删不掉的windous应用商店apps 发现问题 总结 你无法想象新手到底会在什么地方…

银河麒麟服务器操作系统安装SQLite数据库

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低&a…

服务器配置入门教程

问题环境: 现场调试的时候遇到很多离奇的问题,部分设备已经老到需要使用清华同方 Windows XP 系统的接口,所以写下这边记录,本文主要是基础教程。 快速入门常识 服务器基础知识_mezz卡-CSDN博客 基本接口识别 IOIOI-RJ45串口&a…

NLP 笔记:Latent Dirichlet Allocation (介绍篇)

1 问题介绍 假设我们有一堆新闻,每个新闻都有≥1个主题 我们现在只知道新闻的内容,我们希望一个算法,帮我们把这些新闻分类成主题人类可以根据每个每个文章里面的单词判断主题,那计算机怎么做呢? ——>LDA(Latent D…

wps表格怎么加一行详细介绍

刚接触wps表格的小伙伴肯定很多都不知道该怎么去操作吧,肯定也不知道怎么去加入一行来添加文字,为此我们带来了教程,帮助你们了解wps表格怎么加一行。 wps表格怎么加一行: 1、首先去打开wps软件,然后选中里面的行。 …

零基础入门转录组数据分析——绘制差异火山图

零基础入门转录组数据分析——绘制差异火山图 差异分析的火山图(Volcano Plot)在生物信息学数据分析中,特别是在基因表达差异分析中,是一个非常直观和有用的工具。 本教程将从导入的数据结构开始,一步步带大家在R中绘制好看的火山图,最后对火山图进行解读,确保读者理解…

Pandas操作MultiIndex合并行列的Excel,写入读取以及写入多余行及Index列处理,插入行,修改某个单元格的值,多字段排序

Pandas操作MultiIndex合并行列的excel,写入读取以及写入多余行及Index列处理,多字段排序尽量保持原来的顺序 1. 效果图及问题2. 源码参考 今天是谁写Pandas的 复合索引MultiIndex,写的糊糊涂涂,晕晕乎乎。 是我呀… 记录下&#…

夜晚水闸3D可视化:科技魔法点亮水利新纪元

在宁静的夜晚,当城市的霓虹灯逐渐暗淡,你是否曾想过,那些默默守护着城市安全的水闸,在科技的魔力下,正焕发出别样的光彩?今天,就让我们一起走进夜晚水闸3D模型,感受科技为水利带来的…

01_安装VMwareWorkstation虚拟机

环境:Win10 19045 软件版本:VMware-workstation-17.5.1 一、下载链接 Download VMware Workstation Pro 二、安装(无脑下一步) 安装位置自选,最好非系统盘。 增强型键盘驱动自选。 更新自选。 快捷方式自选。 三、…

Spark-Scala语言实战(7)

在之前的文章中,我们学习了如何在IDEA中导入jars包,并做了一道例题,了解了RDD。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢…

windows下QT如何集成OpenCV

说明 我在windows下使用QT Creator12创建的CMake项目,需要OpenCV的一些功能。由于安装的时候我选择的QT组件都是MInGW的,所以无法使用VS studio版本的dll库。 为什么vs的版本不能用 我安装QT选择的是MinGW版本,本地编译QT工程只能选择MinG…

Triton推理服务器部署YOLOv8实战

课程链接:Triton推理服务器部署YOLOv8实战_在线视频教程-CSDN程序员研修院 Triton Inference Server(Triton 推理服务器)是一个高性能、灵活、可扩展的推理服务器,支持多种机器学习框架(PyTorch、ONNX等)和…

服务器被CC攻击之后怎么办?

1.取消域名绑定取消域名绑定后Web服务器的CPU能够马上恢复正常状态,通过IP进行访问连接一切正常。但是不足之处也很明显,取消或者更改域名对于别人的访问带来了不变,另外,对于针对IP的CC攻击它是无效的,就算更换域名攻…

计算机毕业设计Python+Spark知识图谱高考志愿推荐系统 高考数据分析 高考可视化 高考大数据 大数据毕业设计 机器学习 深度学习 人工智能

学院(全称): 专业(全称): 姓名 学号 年级 班级 设计(论文) 题目 基于Spark的高考志愿推荐系统设计与实现 指导教师姓名 职称 拟…

实时语音识别(Python+HTML实战)

项目下载地址:FunASR 1 安装库文件 项目提示所需要下载的库文件:pip install -U funasr 和 pip install modelscope 运行过程中,我发现还需要下载以下库文件才能正常运行: 下载:pip install websockets,pi…