自学内容网 自学内容网

【Linux】进程第三弹(虚拟地址空间)

目录

现象

底层原因

数据不发生修改

数据修改

小总结

地址空间本质

为什么要有地址空间


现象

 来看代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int val = 50;

int main()
{
printf("father process is running,pid is:%d,ppid is:%d,val:%d,&val:%p\n", getpid(), getppid(), val, &val);
pid_t id = fork();
if (id == 0)
{
int cnt = 0;
while (1)
{
printf("child process,val:%d,&val:%p\n", val, &val);
cnt++;
sleep(1);

if (cnt == 3)
{
val = 100;
printf("child proess change 50->100\n");
printf("child process,val:%d,&val:%p\n", val, &val);
sleep(1);
}
}
}

else if (id > 0)
{
while (1)
{
printf("father process,val:%d,&val:%p\n", val, &val);
sleep(1);
}
}


return 0;
}

 如上述代码,定义一个全局变量val,分别在父子进程中打印内容和地址,当在子进程运行3s后,更改其值。来看看结果。

从上图不难看出,没修改前父子进程的val都是50,地址也一样;当在子进程修改后,子进程打印出来了100,而父进程还是50,但是地址还是一样的!!很奇怪!!?

  • 同一变量名输出不同的内容,说明父子进程中用的并不是同一个变量。
  • 每一个变量都会有属于自己的物理空间(地址)。。说明,使用程序代码打印出来的这个地址绝对不是物理地址!
  • 实际上,在Linux中,对于这样的地址就被称为虚拟地址!平时我们采用C/C++打印出来的地址,都是虚拟地址,并不是数据真正在内存中的地址!
  • OS在创建进程时,不仅仅将进程的代码和数据加载到内存,还会为每一个进程创建一个地址空间,这个地址空间就是虚拟地址空间!!

既然是虚拟地址,那又怎么找到当前数据所在内存的位置?只有知道位置,才能进行数据的访问和修改。因为内存和代码被加载到了物理内存中。。

实际上OS也会为每个进程建立一个页表,这个页表能够将虚拟地址和物理地址建立起映射的关系!操作系统会自动将虚拟地址转化为物理地址。。

底层原因

数据不发生修改

  • 这种情况父子进程的页表映射的是同一个地址。因为子进程会继承父进程内容,父进程的代码和数据子进程都能看到(不能修改)

 

数据修改

  • 这种情况会发生OS发生写时拷贝。OS会在内存重新找一块空间,将数据拷贝下来再做修改,并重新更新子进程的页表,重新建立新的映射关系。

为什么要发生写时拷贝?

  • 因为进程具有独立性,多进程运行,都需要独享各种资源,进程之间不能相互干扰。 

能不能在创建子进程的时候就将父进程的数据全部写时拷贝呢?

  • 可以,但是没必要,因为子进程不一定会全部修改父进程的数据,写时拷贝是有一定的消耗的,应该要按需分配,修改的时候在写时拷贝,这样更能节省消耗!

小总结

①上述程序运行的结果足矣说明,同一个变量,地址相同,本质是虚拟地址相同,内容不同本质是被映射到的物理地址不同!

②这也解释了fork函数,同一个id变量名为什么既能>0,又能小于0的原因。因为fork函数会返回两次,即return,return的本质就是写入(修改),发生写时拷贝!

//fork会return,对父进程的id写入,发生写时拷贝!
pid_t id=fork();
if(id==0)
{
    //child
    ....
}
else if(id>0)
{    
    //father
    .....
}

地址空间本质

进程地址空间,在内存中是一种内核数据结构,在Linux中本质就是一个mm_struct的结构体,这个结构体内部的属性都是表示某一个区域的范围。。

就好像一把直尺,每一部分的内容都有属于自己的范围!

为什么要有地址空间

①拦截非法请求,变相保护物理内存! 

  • 如果没有地址空间,进程直接访问物理内存,极大的增加系统异常的概率。
  • 加上地址空间,进程首先得先通过地址空间这一关,比如访问数组越界等异常问题,地址空间会立马报错,阻止进程继续访问物理内存!

②进程管理模块和内存模块的解耦,实际是提高内存利用率。

  • 如果在创建进程的时候,直接申请物理内存。若进程长时间不使用物理内存,那必然会造成资源的浪费
  • 有了虚拟地址空间,可以在页表先申请出虚拟地址,当进程实际需要使用内存,再申请内存空间,并建立映射关系,极大提高内存利用率。

③将无序变有序,让进程以统一的视角看待物理内存以及自己运行的各个区域。

  •  有了地址空间,每个进程都无需关心具体的数据在物理内存中的位置,只需知道虚拟地址,OS会通过页表映射到对应的物理位置!

 好了,今天的内容就分享到这里,如果你有所收获,欢迎三连,xd!


原文地址:https://blog.csdn.net/m0_73470633/article/details/142716498

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!