【Linux】进程的地址空间

一、看现象

复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int g_val = 100;
  5 int main()
  6 {
  7   printf("father process is running!pid: %d,ppid: %d\n",getpid(),getppid(    ));
  8   sleep(1);
  9 
 10   int id = fork();
 11   if(id == 0)
 12   {
 13     int cnt = 0;
 14     while(1)
 15     {
 16       printf("This is child process.pid: %d,ppid: %d,g_val: %d,&g_val: %p    \n",getpid(),getppid(),g_val,&g_val);
 17       sleep(1);
 18       cnt++;
 19       if(cnt == 5)
 20       {                                                                  
 21         g_val = 200;
 22         printf("This is child process.chang %d -> %d\n",100,200);
 23       }
 24     }
 25   }
26   else 
 27   {
 28     while(1)
 29     {
 30       printf("This is father process.pid: %d,ppid: %d,g_val: %d,&g_val: %    p\n",getpid(),getppid(),g_val,&g_val);
 31       sleep(1);
 32     }
 33  }
 34 }

运行以上代码,结果如下

可以看出,起初子进程和父进程都认为g_val值为100,且g_val地址相同,但当一段时间后子进程修改g_val的值后,子进程检测到的g_val值确实是修改后的值,但是父进程检测到的g_val确实修改前的值。这种现象体现出父子进程对数据的处理,我们知道父子进程是具有独立性的。

进程=内核数据结构(task_struct)+代码和数据 ,所以每个进程都应该有自己独立的内核数据结构和代码与数据。我们知道代码是只读的,子进程会继承父进程的数据,但是数据应该是可以修改的,那子进程修改数据不应该影响父进程的数据,所以子进程和父进程的g_val不能是同一个变量。可是为什么&g_val是相同的呢?说明这个地址绝对不是物理地址,我们称其为虚拟地址!!

二、快速理解

在32位平台下,程序的地址空间大小为1G

每个进程都要有自己独立的地址空间及页表,操作系统如何管理地址空间呢?实际上地址空间的本质就是内核中的一个结构体对象,父进程创建子进程时子进程的地址空间会继承父进程地址空间的内容,其中包含了虚拟地址,所以父子进程的虚拟地址是相同的。当发生写入操作时,操作系统会在内存中开辟一块空间并修改子进程中页表的内容,再将修改的值写入该空间,即发生写时拷贝。

为什么会有上述情况发生?因为进程具有独立性,多进程运行,需要独享各种资源,多进程运行期间互不干扰。

如果父子进程不执行写操作,未来一个全局变量,默认是被父子共享的,代码也是共享的(只读的)。要修改的时候还要等待执行浅拷贝的时间,那么我们能不能把数据在创建子进程的时候,直接全部给子进程拷贝一份呢?这种行为是不建议的,因为进程内的数据量很大,并且也不是所有的数据都需要修改,更加浪费空间与时间。所以我们在有写操作时再申请空间更加高效,通过调整拷贝的时间顺序,从而达到有效节省空间的目的。

三、深入理解

1.如何理解地址空间

地址空间本质是内核的一个struct结构体,内部很多的属性都是表示start、end范围

源代码中struct mm_struct部分内容如下

2.为什么要有地址空间

将无序变成有序,让进程以统一的视角看待物理内存以及自己运行的各个区域。在没有地址空间前,进程的代码和数据在内存中的存储可能并不是连续的,这样进程PCB必须记录下其起始地址范围,极其麻烦。 而引入地址空间后,有划分好的代码区、数据区等,进程信息就被存在特定的位置。

**进程管理模块和内存管理模块进行解耦。**当申请了一些堆空间,同时并将物理内存给它,但如果不及时使用,会造成浪费,降低效率。当引入地址空间后,我们先为它开辟地址空间,但先不开辟物理内存,即不通过页表建立映射关系,等到需要的时候再开辟内存,这样内存的使用率也就变得非常高了。

拦截非法请求,对物理内存进行保护。当发生越界访问时,所使用的虚拟地址在页表中并不能找到,没有对于的映射关系,此时操作系统就会拦截这次的访问请求,避免了在物理内存中写入数据而影响其他数据。

相关推荐
小许同学记录成长11 分钟前
几何体编辑与布尔运算
算法·无人机
一起逃去看海吧14 分钟前
dify-03
java·linux·开发语言
fengyehongWorld15 分钟前
Linux 根据端口进行的相关查询
linux
lihao lihao18 分钟前
linux匿名管道
linux·运维·服务器
うちは止水21 分钟前
weston出图调试
linux·wayland·weston
STDD22 分钟前
Farming Simulator 25(模拟农场 25) Linux 专服搭建完全指南
linux·运维·javascript
fanged25 分钟前
简单看看3A算法2(TODO)
算法
智者知已应修善业30 分钟前
【51单片机4位静态数码管显示1234】2023-11-14
c++·经验分享·笔记·算法·51单片机
♡すぎ♡39 分钟前
镜面 IBL 预过滤贴图的计算
算法·计算机图形学·贴图·pbr