进程地址空间

Posted 两片空白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程地址空间相关的知识,希望对你有一定的参考价值。

目录

一.发现问题

二.得出结论

三.实际的进程地址空间

3.1 什么是进程地址空间?

3.2 进程地址空间是怎么工作的?

3.3 为什么存在进程地址空间。

3.4.解决上面问题

四.OS怎么管理进程地址空间

4.1描述

 4.2组织


在博客C/C++内存管理一文中,我们了解到了C/C++的内存管理,大概了解了内存分布情况。实际完整的进程地址空间如下。

 来一段代码验证一下:

    1 #include<stdio.h>
    2 #include<stdlib.h>
    3 int g_val1=1;
    4 int g_val2;
    5 int main(int argc,char *argv[],char *env[]){
    6   //代码区地址
    7   printf("%p\\n",main);
    8   //全局数据区
    9   printf("%p\\n",&g_val1);
   10   printf("%p\\n",&g_val2);
   11 
   12   int *m=(int *)malloc(10);
   13   //堆地址
   14   printf("%p\\n",m);
   15   //栈地址
   16   printf("%p\\n",&m);
   17   //命令行参数地址
   18   printf("p\\n",argv[0]);
   19   //环境变量地址
   20   printf("%p\\n",env[0]);
   21 
   22   return 0;                                                                                                                                            
   23 } 

但是,学了操作系统的进程地址空间后,发现这并不是我们所理解的内存。

一.发现问题

先来一段代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 
  5 int g_val=0;
  6 int main(){
  7   pid_t id=fork();
  8   if(id==0){
  9     printf("i am child... pid:%d, g_val:%d, &g_val:%p\\n",getpid(),g_val,&g_val);
 10     sleep(1);
 11   }
 12   else if(id>0){
 13 
 14     printf("i am father... pid:%d, g_val:%d, &g_val:%p\\n",getpid(),g_val,&g_val);
 15     sleep(1);                                                                                                                                            
 16   }
 17   else{
 18     perror("fork");
 19     exit(1);
 20   }
 21 
 22   return 0;
 23 }

 但是将代码稍加改动,在保证子进程先跑,并且在子进程里改动全局变量g_val的值。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 
  5 int g_val=0;
  6 int main(){
  7   pid_t id=fork();
  8   if(id==0){
  9     g_val=100;                                                                                                                                           
 10     printf("i am child... pid:%d, g_val:%d, &g_val:%p\\n",getpid(),g_val,&g_val);
 11     sleep(1);
 12   }
 13   else if(id>0){
 14     //父子进程顺序有调度器决定
 15     sleep(2);//保证子进程先跑
 16     printf("i am father... pid:%d, g_val:%d, &g_val:%p\\n",getpid(),g_val,&g_val);
 17     sleep(1);
 18   }
 19   else{
 20     perror("fork");
 21     exit(1);
 22   }
 23 
 24   return 0;
 25 }

         这就发现一个问题,如果系统的内存像我们上面那样分布,在同一个地址空间里的内容不可能不一样。(值是先改变的)

二.得出结论

  • 如上问题,同一地址空间的内容不一样,所以,父子进程输出的变量绝对不是同一个变量。
  • 但是他们的地址值相同,所以这个地址绝对不是实际的内存物理地址。
  • 在Linux地址下,这种地址叫做虚拟地址,我们用C/C++语言看到的地址全部都是虚拟地址,物理地址用户看不到,有操作系统管理。
  • 操作系统负责将虚拟地址转化为物理地址

进程地址空间不是实际的内存。

三.实际的进程地址空间

可能你现在还看不懂这张图,看完下面的说明,你再来看可能就很清楚了。 

3.1 什么是进程地址空间?

        进程地址空间是一张描述进程占有资源的一张表。再内核中的体现为一个数据结构mm_struct,将实际内存划分成了各种区域。

3.2 进程地址空间是怎么工作的?

        虚拟地址通过页表映射到实际物理内存上的物理地址。

        页表:页表上保存着虚拟地址与实际内存物理地址的关系,还保存着其它的管理权限。比如一个变量的可读可写权限。虚拟地址通过这张表来对应上内存的实际地址。页表也将内存管理和进程管理分割成了两个独立的部分,便于管理。

        虚拟地址通过页表转化成实际地址动作是操作系统做的。

3.3 为什么存在进程地址空间。

  • 通过虚拟内存实现空间分布更加合理,相同数据放一起。

如果没有进程地址空间,进程都是在物理地址空间上直接开辟,如果一个进程空间地址后面紧跟着一个进程,这个进程要扩容时,可能会导致一个进程的空间不连续。数据在实际内存地址是随便放的。

  • 保护物理地址空间

进程地址空间与实际地址空间通过页表进行转化,有一个一一对应关系。如果有一个野指针访问到了别的地址空间。第一:可能页表上根本没有对应关系,直接保错。第二:页表保存的其它管理权限也可能导致,访问不了,直接报错,所以说将进一步保护了物理地址空间。

3.4.解决上面问题

        为什么父子进程全局变量的地址相同而内容不同?

        因为进程地址空间不是实际的内存,与实际内存存在一种映射关系。每一个进程都会对应一个进程地址空间,父进程创建子进程,子进程拷贝了父进程的进程地址空间,所以两个全局变量g_val的地址相同。当子进程将g_val改变时。操作系统会改变子进程页表中虚拟地址和物理地址的映射关系,重新在实际物理空间开辟一空间,保存改变的值但是进程地址空间没有变化,变化的是实际物理地址。

注意:有多少进程就有多少进程地址空间和页表。

四.OS怎么管理进程地址空间

        每一个进程都会有一个进程地址空间,系统有多个进程,所以系统会有多个进程地址空间,那么进程地址空间是如何被管理的?

                        先描述后组织

4.1描述

        进程地址空间实际也被OS描述成了一个数据结构,mm_struct。这个数据结构的主要作用是将实际内存划分成各种区域。

简单描述一下,表现为:

        C/C++中申请空间的本质是:向实际物理内存索要一空间,得到物理地址。再到得到进程地址空间特定区域没有被使用的虚拟地址,再页表上建立映射关系和其它管理权限,再返回虚拟地址。

 4.2组织

        进程与进程地址空间强相关,每一个进程里都有一个指针指向进程地址空间,OS只要找到进程就找到进程地址空间了。

PS补充:

进程的PCB中包括数据和代码,代码和数据都保存在实际的物理空间。进程的代码都会有一个虚拟地址,进程想执行代码,先拿虚拟地址到页表中找到对应的实际物理地址,找到代码,就可以执行代码了。

以上是关于进程地址空间的主要内容,如果未能解决你的问题,请参考以下文章

Linux进程虚拟地址空间

python多线程

进程的虚拟地址空间,堆栈堆数据段代码段

Linux进程地址空间与进程内存布局详解,内核空间与用户空间

Linux一文讲清进程地址空间

进程和线程和协程之间的关系