Linux从无到有进程的地址空间

Posted 一个正直的男孩

tags:

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

地址空间想必大家听的耳朵都起茧子了,栈区、堆区、静态区……其实这还有很的秘密,或许他会颠覆你的认知,那么就一起来探索吧


文章目录


进程地址空间

我们先回顾一下空间排布如图所示:

这里或许有一个比较新的概念就是栈与堆相对增长,堆往高地址增长,栈往低地址增长,实践出真知,我们来验证一下吧


代码:

int main()

  char *a=(char*)malloc(1);
  char *b=(char*)malloc(1);
  char *c=(char*)malloc(1);
  char *d=(char*)malloc(1);
  char *e=(char*)malloc(1);


  printf("stack:\\n");
  printf("&a= %p\\n",&a);
  printf("&b= %p\\n",&b);
  printf("&c= %p\\n",&c);
  printf("&d= %p\\n",&d);
  printf("&e= %p\\n",&e);


  printf("heap:\\n");
  printf("a= %p\\n",a);
  printf("b= %p\\n",b);
  printf("c= %p\\n",c);
  printf("d= %p\\n",d);
  printf("e= %p\\n",e);
  return 0;


结果:


问:

等等 秋豆麻跌 ,这就是地址空间吗,咋感觉只是和语言层面的呀,Linux不是操作系统的学科吗,这有啥关系呢?

答:

你仔细想想当代码写完运行的过程是啥,是不是把程序载入内存,操作系统会为他创建task_struct也就是进程,再想想执行代码的时候要找变量是不是要通过地址区内存中找



什么是虚拟地址空间 ?

问:
那么我现在问一个你可能觉得可笑的问题

这个虚拟进程地址空间是真正对应在磁盘上的地址吗?

有人说是啊,不然呢?我不是就是通过这个地址访问和操作的吗?难不成我之前的数据没有存在磁盘里吗。


看下这段代码:

当count为3时,改变number

int main()

      int number=10;
      pid_t ret = fork();
      int count=1;
       if(ret==0)
       
          //child
          while(1)
          
              printf("I am  child   number=%d   address=%p  pid=%d  ppid=%d\\n",number,&number,getpid(),getppid());
               if(count==3)
               number=100;
               
             	count++;
               sleep(1);
          

       
       else if(ret>0)
       
         //father
          while(1)
          

              printf("I am  father   number=%d   address=%p  pid=%d  ppid=%d\\n",number,&number,getpid(),getppid());
              sleep(1);
          
       
       else
       
         //filed
       


         return 0;
;

运行结果

或许遗忘)知识补充

进程与进程之间相互独立
进程的所有数据代码都来自于父进程,如果谁修改就会对数据写实拷贝

结果分析

仔细观察你会发现,当打印了三次后,子进程就修改了number的值,进程之间是相互独立的宗旨,子进程会为number重新开块空间,但你仔细看会发现他的地址还是一样的


下定义:

虚拟地址空间其实就是一个抽象的概念,是每一个进程看待内存的方式,且每个进程都觉得自己独占整系统内存资源(画大饼)举个例子:


国外有个富豪,有三个私生子 (富豪标配) ,且相互不知道对方的存在,如何他爹和每个人说不要让我操心乖乖的,以后就继承我花呗欠的钱20亿(🐶),以后你就会拥有10亿家产。此时家产==内存资源进程==私生子富豪==操作系统,那么私生子都觉得自己有10亿家产(进程看待内存的方式,独占系统资源)


且整个虚拟进程地址空间在操作系统层面看其实也是一个结构体mm_struct,里面存了各各区域的起始位置和结束位置

如图所示:


问:

按照概念来看,已经知道了子进程中的number已经重新开辟了一块空间,那为啥地址还是一样啊?(已经知道虚拟地址空间不是内存)

答:

你想既然你知道虚拟地址空间不是真实地址,但是在编译器看到的地址都是虚拟地址空间的地址,且每个进程的进程地址空间都是一样的,那么他和内存(磁盘)中一定有一个转换关系,这个关系就是页表


浅谈页表

如你所看到的一样,他就是虚拟地址空间转换到内存(磁盘) 的桥梁(目前简单了解),他的底层是一个红黑树,那么我们看他是如何运作的吧

知识拓展:

代码:

int main()

    char * ch="hello world";//1
    ch[0]='H';//2

上述代码不用看一定是错的,但你仔细想字符常量开始也是写到磁盘,那么也就是说他是可以修改的,2(代码标号) 修改了为啥会报错呢? 其页表上不止存了地址还有对这个区域 读、写、操作的权限,当你常量的时候他就会吧写的权限给抹去,一旦写九杀了这个程序(程序就是报错或者崩了)


解释
为何子进程改变后地址相同的情况如图所示:
问:

这个难道就是他真正的用处吗?那不要这个进程虚拟地址空间不也可以,改变的时候让操作体统来判断不也可以吗?他的存在真的有意义吗

答:

进程虚拟地址空间存在的真正意义

  1. 方便操作合法性校验(上述例子字符串的修改,其实操作系统来管理效率其实不高)
  2. 让每个进程都以同样的方式看待内存
  3. 内存管理和进程管理进行结藕

问:

啥是内存管理和进程管理进行结耦?

答:

先回顾一下知识,操作系统的主要‘’工作‘’是啥 进程管理,内存管理,驱动管理,文件管理,结耦可以理解协作的意思,如图所示工作原理:

问:

如果没有进程虚拟地址空间可以吗?

答:

说实话早些年(或许10多年前)的电脑还真没有进程虚拟地址空间,既然现在有了,说明没有进程虚拟地址空间会很麻烦

  1. 当一个进程的指针越界访问或者野指针访问的其他进程的数据(进程的独立性无法保障)
  2. 当有恶意程序时,你的电脑可以说就是退掉毛的🐑遇到了🐺直接就开始访问你的物理内存(似乎之前盗qq号也是这个)

知识补充

如何检查是否越界(金丝雀技术

内存管理系统会在你这个进程申请的空间前后设置为cccccc,如何操作系统会看这一段空间是否被修改(这也就是越界没有报错的原因)内存视图如图所示:



以现在的视角在看程序

以前面的知识铺垫我们知道当你个程序跑起来就会被加载到内存在为这个程序创建PCB(tack_struct),那么现在就在此基础上增加了一个进程虚拟地址空间(mm_struct)


唠唠家常

最近的感悟,大环境确实重要,如果身边都是优秀的人你自然会变得优秀(除非自己没追求),如果身边是不上进的所谓近朱者赤近墨者黑(如果有所追求也不会被同化),但是身边如果和你一个水平人很多,这个最麻烦了,那种稍微一下就感觉超过他的优越感是最没有用,也是最罪恶的,这也是停滞不前的一个原因吧

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

X86-64和ARM64用户栈的结构

如何通过多进程共享(或排除共享)全局变量?

Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列

Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列

Linux进程概念——下验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列

<Linux>进程地址空间