0


Linux进程学习【进程地址】

✨个人主页:****Yohifo
🎉所属专栏:****Linux学习之旅
🎊每篇一句:****图片来源
🎃操作环境:****CentOS 7.6 阿里云远程服务器

  • Perseverance is not a long race; it is many short races one after another.- 毅力不是一场漫长的比赛;是许多短跑一个接一个。

四姑娘山


文章目录


📘前言

**对于

C/C++

来说,程序中的内存包括这几部分:

栈区

堆区

静态区

等,其中各个部分功能都不相同,比如函数的栈帧位于

栈区

,动态申请的空间位于

堆区

,全局变量和常量位于

静态区

,区域划分的意义是为了更好的使用和管理空间,那么

真实物理空间

也是如此划分吗?

多进程运行

时,又是如何区分空间的呢?

写时拷贝

机制原理是什么?本文将对这些问题进行解答**

内存条:真实的物理空间,用来存储各种数据

内存条


📘正文

📖问题引入

地址是唯一的,对地址进程编号的目的是为了不冲突

**这是个耳熟能详的概念,在

C语言

学习阶段,我们可以通过对变量

&

取地址的方式,查看当前变量存储空间的首地址信息**

#include<stdio.h>intmain(){constchar* ps ="这是一个常量字符串";printf("字符串地址:%p\n", ps);//%p 专门用来打印地址信息return0;}

结果

**利用前面学习的

fork

函数创建子进程,使得子进程和父进程共同使用一个变量**

#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<stdlib.h>intmain(){int val =10;
  pid_t id =fork();if(id ==0){
      val *=2;//刻意改变共享值printf("我是子进程,pid:%d ppid:%d 共享值:%d 共享值地址:%p\n",getpid(),getppid(), val,&val);exit(0);}waitpid(id,0,0);printf("我是父进程,pid:%d ppid:%d 共享值:%d 共享值地址:%p\n",getpid(),getppid(), val,&val);return0;}

结果
对于同一块空间,读取到了不同的值,是不可能出现这种情况的

因为真实地址都是 **

唯一

** 的,分析:

  • 不同的空间出现同名的情况
  • 父子进程使用的真实物理空间并非同一块空间!

原因:

  • 当子进程尝试修改共享值时,发生 写时拷贝 机制
  • 语言层面的程序空间地址不是真实物理地址
  • 一般将此地址称为 虚拟地址线性地址

结论:语言层面的地址都是虚拟地址,用户无法看到真实的物理地址,由 OS 统一管理


📖虚拟空间划分

一般用户的认知中,

C/C++

程序内存分布如下图所示,直接表示内存中的各个部分
虚拟空间


📖真实空间分布

但实际上的空间分布是这样的:
真实分布
如果有多个进程(真实地址空间只有一份),此时情况是这样的:
多进程情况

🖋️代码实现

**在实现虚拟地址空间时,是用结构体

mm_struct

实现的**

task_struct

一样,

mm_struct

中也包含了很多成员,比如不同区域的边界值

//简单展示其中的成员信息
mm_struct
{//代码区域划分unsignedlong code_start;unsignedlong code_end;//堆区域划分unsignedlong heap_start;unsignedlong heap_end;//栈区域划分unsignedlong stack_start;unsignedlong stack_end;//还有很多其他信息
    ……
}

**每个进程都会有这样一个

mm_struct

,其中的区域划分就是虚拟地址空间**

通过对边界值的调整,可以做到不同区域的增长,如堆区、栈区扩大

**

mm_struct

中的信息配合

页表
  • MMU
    
    在对应的真实空间中使内存(程序寻址)**

🖋️问题反思

此时可以理解为什么会发生同一块空间能读取到不同值的现象了

  • 父子进程有着各自的 mm_struct,其成员起始值一致
  • 对于同一个变量,如果未改写,则两者的虚拟地址通过 页表 + MMU 转换后指向同一块空间
  • 发生改写行为,此时会在真实空间中再开辟一块空间,拷贝变量值,让其中一个进程的虚拟地址空间映射改变,这种行为称为 写时拷贝

刚开始,父子进程共同使用同一块空间
改写前
当子进程修改共享值后

写时拷贝


📖进程地址空间

**下面来好好谈谈

进程地址空间

(虚拟地址)**

🖋️虚拟地址

在早期程序中,是没有虚拟地址空间的,对于数据的写入和读取,是直接在物理地址上进行的,程序与物理空间直接打交道,存在以下问题:

  • 假设存在野指针问题,此时可能直接对物理内存造成越界读写
  • 程序运行时,每次都需要大小为 4GB 的内存使用,当进程过多时,资源分配就会很紧张,引起进程阻塞,导致执行效率下降
  • 动态申请内存后,需要依次释放,影响整体效率

野指针行为

**为了解决各种问题,大佬们提出了

虚拟地址空间

这个概念**,有了

虚拟空间

后,当进程创建时,系统会为其分配属于自己的

虚拟空间

,**需要使用内存时,通过

寻址

的方式,使用物理地址上的空间即可**

  • 多个进程互不影响,动态使用,做到 效率资源 双赢
  • 发生越界行为时,寻址 机制会检测出是否发生越界行为,如果发生了,能在其对物理地址造成影响前进行拦截
  • 因为每个进程都有属于自己的空间,OS 在管理进程时,能够以统一的视角进行管理,效率很高

光有 虚拟地址空间 是不够的,还需要一套完整的 ‘‘翻译’’ 机制进行程序寻址,如

Linux

中的

页表

+

MMU

🖋️页表+MMU

**

页表

本质上就是一张表,

操作系统

会为每个

进程

分配一个

页表

,该

页表

使用

物理地址

存储。当

进程

使用类似

malloc

等需要

映射代码或数据

的操作时,

操作系统

会在随后马上

修改页表

以加入新的

物理内存

。当

进程

完成退出时,内核会将相关的页表项删除掉,以便分配给新的

进程

**
原话出处:ARM体系架构——MMU

**系统底层机制的研究是非常生涩的,这里简言之就是

页表

记录信息,通过

MMU

机制进行寻址使用内存**,假设目标空间为只读区域(比如数据段、代码段),在进行空间开辟时,会打上只读权限标签。后续对这块进行写入操作时,会直接拒绝

对于这种机制感兴趣的同学可以点击下面这几篇文章查看详细内容:
Linux的虚拟内存详解(MMU、页表结构)
ARM体系架构——MMU
逻辑地址、页表、MMU等

🖋️写时拷贝

**

Linux

中存在一个很有意思的机制:

写时拷贝

**
这是一种 赌bo 行为,

OS

此时就赌你不会对数据进行修改,这样就可以 **使多个

进程

在访问同一个数据时,指向同一块空间,当发生改写行为时,再新开辟空间进行读写**

这种行为对于内置类型来说感知还不是很强,但如果是自定义类型的话,**

写时拷贝

行为可以在某些场景下减少

拷贝构造

函数的调用次数(尤其是

深拷贝

),尽可能提高效率**

可以通过一个简单的例子来证明此现象

//计算 string 类的大小#include<iostream>#include<string>usingnamespace std;intmain(){
    string s;
    cout <<sizeof(s)<< endl;return0;}

对比
原因:

  • g++ 中的 string 对象创建后,它就赌你不会直接改写,所以实际对象为一个指针类型(64位环境下为8字节),当发生改写行为时,触发 写时拷贝 机制,再进行其他操作

🖋️内存申请

**值得注意的是,在进行动态内存申请时,

OS

也并非直接去申请好内存,而是先判断是否有足够的内存,如果有,就在

页表

中记录相应信息(这种行为叫做

缺页中断

),当程序实际使用到这块空间时,

OS

才会去申请内存给程序使用**

OS是一个讲究人,不允许任何空间浪费或低效率行为

**假设没有

缺页中断

机制,给程序分配空间后,程序又不用,此时空间属于闲置状态,这是不被

OS

认可的低效浪费行为**

缺页中断
图片来源:3.2.2 OS之请求分页管理方式(请求页表、缺页中断机构、地址变换机构)


📖虚拟地址空间存在的意义

**总结一下,

虚拟内存+页表+MMU

这种管理方式的好处:**

  • 防止地址随意访问,保护物理内存与其他进程(权限设置)
  • 进程管理内存管理 进行 解耦,方便 OS 进行更高效的管理
  • 可以让进程以统一的视角看待自己的代码和数据

📘总结

**以上就是本篇关于

Linux

进程学习【进程地址】的全部内容了,我们从一个有趣的小问题切入,见识到了

虚拟地址空间

物理地址空间

的奇妙关系,在种种机制的加持之下,

OS

对进程的管理变得更加得心应手,系统也因此得以高效运行**

如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐

Linux进程学习【环境变量】

Linux进程学习【进程状态】

Linux进程学习【基本认知】

===============

Linux工具学习之【gdb】

Linux工具学习之【git】

Linux工具学习之【gcc/g++】

Linux工具学习之【vim】

承蒙厚爱,感谢支持

标签: linux 学习 运维

本文转载自: https://blog.csdn.net/weixin_61437787/article/details/129438345
版权归原作者 夜 默 所有, 如有侵权,请联系我们删除。

“Linux进程学习【进程地址】”的评论:

还没有评论