[Linux打怪升级之路]-秒懂进程地址空间
创始人
2024-03-13 07:54:48
0

前言

作者:小蜗牛向前冲

名言:我可以接受失败,但我不能接受放弃

如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。

目录

一、C/C++下的地址空间

1、回忆C/C++下的内存分布

2、看一个现象

二、虚拟地址空间

1、浅谈虚拟地址空间

2、图解进程空间


本期学习目标:了解C/C++下的地址空间,操纵系统下的进程地址空间

我们在学习进程地址空间的时候,要先回忆一下C/C++下的地址空间。

一、C/C++下的地址空间

1、回忆C/C++下的内存分布

查看源图像       我们在学习C/C++的时候常说全局变量和局部变量存在数据段,只读常量的数据存在代码段,自己也可以为变量的申请空间我们称为堆区,还有一个区域就做栈区主要存放函数的返回值/函数的参数/非静态的成员变量。但是我们常常说这就是计算机内的内存分布,但他们真的就是指磁盘中的内存分布吗?我们要内存是直接在磁盘在申请的吗?

在回答这些问题之前我们先在Linux下跑一段代码,见见一个难以理解的现象。

2、看一个现象

我们写一个代码,用fork创建子进程,并循环打印子进程和父进程,让子进程和父进程都先打印出

 global_value的值和 global_value的地址,当cet==5的时候,子进程改变 global_value的值为300,父进程不变,观察子进程和父进程的变化。

#include 
#include int global_value = 100;int main()
{pid_t id = fork();if (id < 0){printf("fork error\n");return 1;}else if (id == 0){int cnt = 0;while (1){printf("我是子进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);sleep(1);cnt++;if (cnt == 5){global_value = 300;printf("子进程已经更改了全局的变量啦..........\n");}}}else{while (1){printf("我是父进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);sleep(2);}}sleep(1);
}

这里我们发现一个很奇怪的现象为什么我们子进程global_value的值明明变为了300但是他的地址确实和100的地址是一样的。这是为什么?

如果我们继续按照在C/C++上进行理解,指针指向的地址空间是唯一的。这就显然和这里的结果相矛盾一个相同的地址这么可能存放相同的值。这说明这里的指针绝对指向的不是物理地址,要解释这个现象我们就不得不继续往下学习虚拟地址空间。

二、虚拟地址空间

1、浅谈虚拟地址空间

什么是虚拟地址空间呢?我相信有许多人都会有疑惑为什么会出现虚拟地址空间怎么个东西,这个问题下面会回答。但在此时我们要形成一个共识:一个进程会认为他独占系统资源(实际上并不是如此)。

我们都知道在C/C++的理解下,我们都认为有一块空间里面存放我们要执行的代码,但是在一个系统中不可能仅仅只有一个进程都在运行,那么也就说进程可能都把他们的要存储的内容分配到同一个空间中(因为进程们都认为,独占空间),那怎么显然会出错,但是在同一个系统中仍然存在多个进程同时在运行。那系统是怎么解决这个问题的呢?

其实我们由此也可得出每个进程都有自己的内存空间,但是真正的空间分布是由操作系统完成的,而那个进程自己的空间,我们称为进程地址空间(也就是虚拟地址空间)。

那么问题来,这个进程空间是怎么规划空间的呢?

我们要管理一个进程都是:先描述,在组织

一个进程的空间他的基本大小空间是字节,我们拿32位的机器来下,将能形成2^32的编码,每个编码都是一个地址,也就形成了4G的空间。对于PCB来说有存一个指针,指向一个结构体的struct mm_struct,而这个结构体内就规划了各个空间的起始地址和终止地址。

struct mm_struct
{//代码区size_t code_start, code_end;//数据段size_t data_start, data_end;//堆区size_t heap_start, heap_end;//栈区size_t stack_start, stack_end;
};

 这样各个内存的区域就描述好了,我们就可以通过PCB中的指针管理这个结构体从而管理进程地址空间。

2、图解进程空间

 从上面图我们看到了什么?

当我们执行my.exe文件的时候,首先是从磁盘中把要执行的代码导入到内存中,pcb就会所到my.exe要执行的指令,就会通过mm*指针让进程地址空间为my.exe分配好空间,在通过一个叫页表的结构和物理内存沟通,在真正的物理内存开好相应的空间。

从上面我们就知道以前我们说指针指向的内存空间,其实是虚拟地址空间。

理解了什么是进程地址空间,下面我们就解释一下为什么会出现我们前面讲的这个现象。

我们子进程global_value的值明明变为了300但是他的地址确实和100的地址是一样的。 

 在global_value为变为300的时候,进程物理空间的指向是上图,这里简单解释一下:子进程是以父进程的模板生成的,所以他最初进程空间通过页表和物理内存的联系都应该是一样的,这也就导致子进程和父进程的global_value的地址都是一样的,但是当我们通过子进程改变global_value的值而父进程不变时。就会发现下图的变化:

 这里我们明白:进程具有独立性。这就要求子进程的改变不能影响父进程。

这时就操作系统就会发生写时拷贝,为子进程的global_value重新在物理内存开辟一块空间给他进行存储,但是进程空间中的地址并没有发生改变,只是子进程的页表的映射改变了,指向了300的空间地址。

而我们对global_value取地址仅仅是得到的是进程空间的地址,这也就导致了为什么二个不同的值存储的空间会一样。

写时拷贝:父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本,后在让进程改变。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...