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

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

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

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

相关推荐
异常君3 分钟前
Java 中 String 的不可变性与 final 设计:核心原理与性能实践
java·面试·代码规范
耀耀_很无聊3 分钟前
03_跨域问题解决
java·spring boot·跨域·satoken
hshpy10 分钟前
LangChain in java
java·langchain·flask
xtmatao10 分钟前
JAVA开发工具——IntelliJ IDEA
java·ide·intellij-idea
Lw老王要学习32 分钟前
Linux容器篇、第一章_02Rocky9.5 系统下 Docker 的持久化操作与 Dockerfile 指令详解
linux·运维·docker·容器·云计算
寒山李白33 分钟前
Spring Boot面试题精选汇总
java·spring boot·后端·面试
橙子小哥的代码世界1 小时前
【大模型RAG】Docker 一键部署 Milvus 完整攻略
linux·docker·大模型·milvus·向量数据库·rag
_可乐无糖1 小时前
EC2安装WebRTC sdk-c环境、构建、编译
服务器·webrtc·aws
蒙奇D索大1 小时前
【11408学习记录】考研数学攻坚:行列式本质、性质与计算全突破
笔记·学习·线性代数·考研·机器学习·改行学it
BillKu1 小时前
Java解析前端传来的Unix时间戳
java·前端·unix