欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Linux之进程地址空间

Linux之进程地址空间

2025/6/5 12:50:22 来源:https://blog.csdn.net/qq_55958734/article/details/139969136  浏览:    关键词:Linux之进程地址空间

目录

程序地址空间 

进程地址空间

为什么要创建程序地址空间呢?

总结


我们已经学习完了进程的所有基本概念,本期开始,我们将更深入的探讨进程相关的内容。我们知道,程序刚开始是存储在磁盘上的,当程序被执行时,会被加载到内存成为进程,进程即是操作系统为管理进程所创建的数据结构和进程相关的代码和数据。那么进程肯定会去访问自己的代码和数据,代码和数据肯定是存储在物理内存中的,那么进程可以直接去访问物理内存中的代码和数据吗?直接访问会不会出现什么问题呢?本期我们将围绕着这些问题来展开讨论。

程序地址空间 

想必大家之前或多或少都听过内存空间的划分,真个内存空间由下往上(由低地址到高地址)分为,代码区(存放函数的地址,函数地址一般情况下就是函数名称),字符常量区(比如常见的常量字符串),未初始化全局数据区,已初始化全局数据区,堆区(由下往上地址由低到高),栈区 (由上往下地址由低到高)等等。一般情况下我们认为内存就分为这几个区。内存分区图如下图所示。

我们以代码作为验证:

#include<stdio.h>
#include<stdlib.h>int data1;
int data2=100;
int main()
{const char* ch="hello world";char* ch2=(char*)malloc(10);int a = 18;printf("%p\n",main);printf("%p\n",ch);printf("%p\n",&data1);printf("%p\n",&data2);printf("%p\n",ch2);printf("%p\n",&a);return 0;
}

 运行结果如下:

根据结果得出结论,各个区的地址确实是按照我们上述讲述的由下往上,由低地址向高地址排列。

进程地址空间

其实上述讲述的程序地址空间的概念并不是完全正确的,准确来说并没有程序地址空间这个概念。有的是进程地址空间这个概念。下来我们要仔细探讨一下进程地址空间的概念。我们先看下述代码。

#include<stdio.h>
#include<unistd.h>int a = 100;
int main()
{if(fork()==0){printf("#############我是子进程更改前a的值等于%d ###########\n",a);a = 200;printf("#############我是子进程更改后a的值等于%d 地址为%p \n",a,&a);}else{printf("##############我是父进程,a的值等于%d 地址为%p \n",a,&a);sleep(2);}return 0;
}

运行结果如下:

父子进程中的变量a的地址,这个地址是物理地址吗?

咦,是好像也不是。直接给出结论,不是。

为什么不是呢?

我们直接看地址,我们发现父子进程打印出来的地址竟然是相同的,如果这个地址是物理地址,也就意味着同一块物理地址,打印同一变量,但是两次打印出来的值确实不相同的。这可能吗?肯定不是可能的,所以这个地址肯定不会是物理地址,那么这个地址到底是什么呢?这就要引出我们本期的重点,进程地址空间。

进程地址空间,我们称它为操作系统为了实现某些目的,创建出来的一块虚拟地址空间,这个虚拟地址空间其实就是上述的程序地址空间。 

为什么要创建程序地址空间呢?

大家参考这样一个场景,如果没有这个进程地址空间,进程直接访问物理内存,假如现在有两个进程,进程a和进程b,假如说进程a是一个记账进程,如果当前进程的数据被写到了内存之后,因为进程可以直接访问物理内存,所以进程b也就会访问进程a的代码和数据,这样就会导致进程a的数据被进程b非法访问,所以为了进程之间的独立性与安全性,我们就不能直接让进程直接去访问物理内存,所以就引入了进程地址空间的概念。

但是这个进程地址空间是虚拟的,并不是真是的,只是我们为了数据的安全性以及为了方便管理才设置的一层软件层。进程地址空间的大小是4GB。进程地址空间也属于操作系统为了管理进程所创建的数据结构,我们称它为struct mm_struct。这是继我们学完task_struct之后又学习的一个进程相关数据结构。我们知道进程控制块pcb是掌管进程相关的属性数据的,所以进程地址空间也是被进程控制块pcb所掌管的,我们可以通过进程控制块pcb访问到进程地址空间。通过示意图为大家展示进程控制块和进程地址空间以及物理地址空间的关系:

以上仅供参考,仅仅是告诉大家虚拟地址和物理地址的对应关系。

1.对进程的操作进行权限管理,保护物理内存和各个进程的数据安全。

首先,程序被加载到内存成了进程,代码和数据通过页表以及mmu建立了物理内存和虚拟地址空间(虚拟内存)的一一映射关系。CPU运行程序的代码怎样找到代码的入口呢,即就是将物理地址中的main函数地址通过页表与代码区联系起来,这样CPU就可以通过访问进程的虚拟地址空间中的代码区就可以找到main函数的地址,从而找到代码的入口。

其次,当物理内存与虚拟内存建立一一映射关系之后,进程通过虚拟地址以及页表的对应关系要访问物理内存时,如果页表中的权限对物理内存中的数据是只读权限,但是当前进程要对数据进行写操作时,操作系统就会为了保护物理内存中的数据安全从而终止当前进程。而且有了页表的对应关系之后,上述的进程a和b就不会存在非当前进程要访问当前进程数据的情况。

2.隐藏物理内存的申请细节,将进程对内存的读写与操作系统对内存的申请进行解耦,实现软件层面上的分离。

这句话怎么理解呢?通过一个场景为大家解释。

有这样一个场景,假如今天学校要求你买书,这本书是1000元,回家之后,你问你爸要钱买书,当你爸同意之后,相当于你已经知道了这1000块钱你爸一定会给你,但是由于你是第二天要,所以这1000块钱的来源就有多种可能,其一就是你爸没有1000块钱,但是他找别人借了,还有就是拿着200块钱出去打麻将,最终赢了1000块钱,但是第二天他将这1000块钱给你的时候,你是不知道这1000块钱是怎么来的,你只关心的是他给了你1000块钱,所以当你使用这1000块钱的时候,你是不关系这1000块钱是怎么来的。所以,在进程地址空间上,就可以理解为,进程只关心内存的读写,不关心操作系统是如何进行内存申请的。内存不够,会将物理内存上的代码和数据采用内存置换算法,将这些代码和数据存储到磁盘上,从而将物理内存空出来,供操作系统为当前进程去申请内存。

3.站在CPU和应用层的角度,进程可以同意看做有4GB的内存空间,每个空间的相对区域位置,是相对确定的,但是虚拟地址所映射的物理地址是不同的。最终达到了一个目标,所有进程都认为自己是独占4GB的内存空间的,达到了进程的独立性。

通过示意图为大家展示。

通过示意图我们可以明显的看到,两个进程的虚拟地址是可以一样的,并不会产生神什么不利的影响,只要通过页表映射时,虚拟地址通过mmu映射的物理地址不一样就可以 。

每个进程都认为自己有4GB的空间,但是有小伙伴就会问了,万一只有4GB的物理地址空间,但是现在有两个进程都要申请4GB的空间,这样不就会产生问题吗?

其实是不会的。首先,操作系统是老大,它具有拒绝的权限,进程认为自己有4GB的空间和操作系统拒绝申请这么大的空间是不冲突的,而且,进程申请空间是有先后的,如果你当前进程申请空间之后不立即使用,那么操作系统就先不会为你当前进程进行申请,会为要立即使用的进程进行内存空间的申请。而且如果两个进程都要理解使用,也没有问题,进程申请空间有先后,第一个进程申请完4GB的空间之后,物理内存是不够用了,但是可以采用内存置换算法,将第一个进程的代码和数据置换到磁盘上,这样就会为第二个进程空出对应大小的空间,所以这样是不会产生问题的。

总结

以上便是进程地址空间的所有内容,再回到刚开始的问题,为什么父子进程的地址是一样的,但是打印出来的值却是不同的,此时我们就全然了解了,这个地址并不是物理地址,而是虚拟地址,因为子进程会继承父进程的进程相关数据结构,也就会继承pcb和mm_struct这两个数据结构,所以父子进程的虚拟地址完全是可以相同的,但是为了保证进程的独立性,父子进程的数据是进行了写时拷贝的,所以也就会通过页表和mmu映射到不同的物理内存上,这就是为什么我们看到的两个变量的地址一样,但是值却不一样。

本期内容向大家讲述了进程地址空间的概念,以及为什么要产生进程地址空间和进程地址空间和物理内存的区别。

本期内容到此结束^_^

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词