【Linux】进程地址空间

什么是进程地址空间?

我们来看一下这个例子:

复制代码
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>

int main()
{
    int val=1000;
    printf("I am father process,pid:%d, ppid:%d\n",getpid(),getppid());
    sleep(5);
    pid_t id =fork();
       if(id==0)
       {
          while(1)
          {
              val=200;
              printf("I am child process,pid:%d, ppid:%d, val:%d,   &val:%p\n",getpid(),getppid(),val,&val);
              sleep(1);
          }
      }
      else
      {
          while(1) 
          { 
              printf("I am father process,pid:%d, ppid:%d, val:%d,  &val:%p\n",getpid(),getppid(),val,&val);
              sleep(1);
          }
      }
}

运行结果:

根据之前的学习我们已经知道进程是有独立性的 ,所以在我们可以理解父子进程中val的值可以不一样,但是我们发现父子进程的val变量的地址是一样的,这是为什么?

从上面的运行结果来看,我们得到的val的地址不是真实的地址 (物理地址),因为同一时间内一个物理地址中只能存储一个进程的数据,不同进程的不同数据不可能同时存在于同一个物理内存中;

实际上OS会给每个进程创建一个独立的虚拟地址空间,通过页表将虚拟地址空间与物理地址一一对应;用户只能得到虚拟地址空间中的虚拟地址,当我们修改虚拟地址中的数据时,操作系统会先通过页表找到对应的物理内存,然后修改物理内存中的数据。

所以父子进程的val变量的地址 其实就是虚拟地址;因为子进程完全的父进程的拷贝;在子进程的val没有改变时,父子进程中val的虚拟地址都是0x7ffc83547fa8,通过页表找到的物理内存也都是一样的0x1122334455,如果子进程对val进行修改,那么OS就会在物理内存中寻找一块新空间,然后将原空间的数据拷贝到新空间,再修改子进程的页表映射关系,(也就是将原来的0x112233445修改为现在的物理地址)最后再修改新空间中 g_val 的值,上述过程叫做 写时拷贝。在这个过程中,我们并没有修改val对应的虚拟地址,所以我们查询到的地址是一样的;

根据上述的例子,我们就可以知道进程地址空间就是虚拟地址空间;

注:在OS中,进程地址空间中的地址通常也被称为线性地址,因为它是按比特位从全0到全1依次顺序编址的;磁盘程序内部的地址通常被称为逻辑地址;在Linux中,三者的意思是一样的,都表示虚拟地址,不用过于区分。

为什么要有地址空间?

上面我们知道的什么是地址空间,了解的地址空间是怎么管理的,那为什么要有地址空间呢?为什么不能直接把数据存放到物理内存中,反而要通过页表,地址空间一一映射到物理内存中?

是为了保护物理内存不受到任何进程内地址的直接访问,保证了数据的安全性;在虚拟地址到物理地址的转化过程中方便进行合法性校验;

如果进程直接访问物理内存,那么看到的地址就是物理地址,而语言中有指针,如果指针越界了,一个进程的指针指向了另一个进程的代码和数据,那么进程的独立性,便无法保证,因为物理内存暴露,其中就有可能有恶意程序直接通过物理地址,进行内存数据的篡改,如果里面的数据有账号密码就可以改密码,即使OS不让改,也可以读取。

  • 进程地址空间保证了数据的安全性

OS会给每个进程创建一个独立的虚拟地址空间,通过页表将虚拟地址空间与物理地址一一对应,当用户对某一进程的虚拟内存越界访问或者非法读取与写入时,页表或操作系统可以直接进行拦截,从而保证了内存中数据的安全

  • 进程地址空间保证了进程的独立性

对于互不相关的两个进程来说,它们都拥有自己独立的地址空间以及页表,页表会映射到不同的物理内存上,磁盘代码和数据加载到内存中的位置也不同,一个进程数据的改变不会影响另一个进程;

以上是进程地址空间的一部分,后续内容会慢慢补充!

相关推荐
小小的木头人1 小时前
Windows Docker desktop 部署
运维·kafka
黑翼杰克斯1 小时前
如何裁剪u-boot,保留其必要功能,使体积尽可能小
linux·1024程序员节
fat house cat_2 小时前
【netty】基于主从Reactor多线程模型|如何解决粘包拆包问题|零拷贝
java·服务器·网络·netty
wanhengidc2 小时前
云手机和虚拟机的区别都有哪些?
运维·服务器·安全·智能手机·玩游戏
williamdsy2 小时前
【Docker】[特殊字符] Docker 部署完全指南 - 从本地开发到云服务器
服务器·docker·开发部署流程
黑科技Python2 小时前
生活中的“小智慧”——认识算法
学习·算法·生活
Yupureki2 小时前
从零开始的C++学习生活 16:C++11新特性全解析
c语言·数据结构·c++·学习·visual studio
青云交2 小时前
Java 大视界 -- Java 大数据在智能教育学习社区互动模式创新与用户活跃度提升中的应用(426)
java·大数据·学习·flink 实时计算·智能教育社区·互动模式创新·用户活跃度
神奇的海马体3 小时前
Tomcat隐藏版本号
java·tomcat
码农周3 小时前
从零搭建vsftpd服务器:避坑指南+实战解决方案
运维·服务器