【Linux】详细理解进程地址空间

研究背景:kernel 2.6.32   32位

程序地址空间

先看一段代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int g_val = 0;

int main(){
   pid_t id = fork();
   if(id < 0){
     perror("fork");
     return 0;
   }
   else if(id == 0){
      g_val = 100;
      printf("child[%d]: %d : %p \n",getpid(),g_val,&g_val);
   }
   else{
     sleep(3);
     printf("parent[%d]: %d : %p \n",getpid(),g_val,&g_val);
   }
   return 0 ;                                                                                        }

明明子进程时先于父进程运行的,并且对 g_val 的值进行了修改,为什么父进程运行的时候 g_val 的值反而还是 0?并且子进程和父进程中的 g_val 变量所存在的地址都一样的?

得出结论:

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

地址一样,说明该地址绝对不是物理地址

在Linux环境下,这个地址叫做 虚拟地址,我们在用 C/C++ 看到的地址,全部都是虚拟地址! 物理地址,用户一概看不到,由 OS 统一管理;

进程地址空间

 

上面的这张图就很好的解释了上面的那段代码:同一个变量,地址相同,起始就是虚拟地址相同,内容不同其实是被映射到了不同的物理地址。

操作系统通过 mm_struct 这个结构体个进程描述了一个虚拟的地址空间;

mm_struct{
    ulong size;
    ulong code_size;
    ulong code_end;
    ulong data_start;
    ulong data_end;
};

虚拟地址空间又通过 页表 对于到相应的物理内存上,但是实际上在物理内存的管理是通过 页段式内存的管理方式 管理的;

                     

虚拟地址空间 + 页表:提高了内存的利用率、对内存访问进行控制;

虚拟地址空间 + 页表 优点:保持了进程的独立性、充分利用内存、内存访问控制

写时拷贝技术

父进程创建子进程创建了子进程,但是并没有直接给子进程开辟空间,拷贝数据。而是跟父进程映射到同一块内存空间,但是 如果数据发生了变化,那么就需要重新给子进程开辟内存,并且更新页表信息,提高子进程的创建性能。

                      

发布了79 篇原创文章 · 获赞 28 · 访问量 7749

猜你喜欢

转载自blog.csdn.net/weixin_43753894/article/details/100160407