当前位置: 首页 > news >正文

【linux】程序地址空间

目录

一、前言

二、进程地址空间

三、扩展

四、总结


一、前言

        我们在学习C/C++的时候或多或少都会提到关于内存分布的相关知识,例如静态成员变量存放在静态区,局部变量存放在栈区等等,那么这些空间是怎么进行排布的呢?我们在运行程序时是直接根据数据在内存的地址来调用的吗?

二、进程地址空间

程序地址空间也可以叫做进程地址空间,因为程序运行起来就是一个进程。

先来看一张空间布局图

这是我们平时为了更好理解而虚构出来的空间布局,实际上这些数据和代码并不是按照这种排布在内存中的,下面会说明为什么。

我们先来看一段代码,并理解运行结果为什么是这样。

#include<stdio.h>    
#include<unistd.h>    
#include<sys/types.h>    
int val = 100;    
int main()    
{    pid_t id = fork();    if(id==0)    {  //子进程  sleep(1);    val+=5; //修改val                                                                                                                                                                     printf("child:pid %d val=%d addr=%p\n",getpid(),val,&val);    }    else    {    //父进程sleep(1);    printf("father:pid %d val=%d addr=%p\n",getpid(),val,&val);    }    return 0;    
}    

1.还可以发现他们之间val的地址是相同的,为什么会有这种现象呢?

首先这个地址肯定不是物理地址,如果是物理地址的话,指向相同空间,一方改变另一方也会发生改变。

在linux下这种地址被称为虚拟地址,并且我们写的C/C++程序中的地址也是虚拟地址,也就是说系统会根据虚拟地址进而转换位物理地址对数据进行访问,有一张对应虚拟地址与物理地址一一对应的表,在操作系统中这个表被称为页表。

为什么父进程与子进程的val的地址相同,因为创建的子进程会将父进程的代码和数据进行拷贝,当然也就把地址啥的都拷贝下来了,类似于浅拷贝,所以我们看到的地址是虚拟地址并且相同。

2.通过观察可以发现变量val在子进程中已经被改变了但是父进程中val的值未被改变。

每个进程都会有自己独立的虚拟地址空间,也有自己对应的页表。

我们上面讲的进程地址空间其实就是虚拟地址空间,是操作系统为了更好的管理并排列好每个区域的数据(例如常量在常量区等等)。使用虚拟地址就能够更好的进行区域划分,那么操作系统是怎么进行区域划分的呢?

在操作系统的task_struct中会维护着指向mm_struct的指针,而mm_struct就是进行虚拟地址空间区域划分(管理)的。

在mm_struct中定义的各个区域的范围,通过更改这些变量就可以对区域进行划分和修改。

    unsigned long total_vm, locked_vm, shared_vm, exec_vm;unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;

在mm_struct中还存在着 

这个是用来管理每个区域的,例如管理栈区的地址范围,相当于是一个别墅的管家。

这个时候我们再来解释为什么子进程与父进程的的val互不影响。

我们知道进程之间是相互独立的,当某个进程对数据进行改变时就会发生写实拷贝,写时拷贝的触发条件就是当某个进程对数据进行修改时。

写实拷贝的过程:

所以我们看到的现象就是这样形成的。

三、扩展

系统怎么对页表进行管理:先描述,再组织

系统会维护一个关于页表信息的结构体,类似于对进程的管理一样的操作。

页表是用来做虚拟地址与物理地址的映射的。

我们可以不先加载代码和数据,只有task_struct,mm_struct等等,因为系统是通过查页表来进行访问数据和代码的,可以先有虚拟地址,然后再将物理地址一个一个填入页表中。

为什么我们对常量进行修改,程序缺崩溃了?因为在页表中还会维护着对每个数据的权限信息,如果我们对该数据进行的非权限访问或修改,系统就会做出相应的操作。

四、总结

为什么系统要使用虚拟地址空间?直接对物理内存的数据进行操作不行吗?

如果直接对内存进行操作,我们创建子进程时,就必须对父进程的数据和代码进行拷贝到内存,这样就会造成空间浪费。

使用虚拟地址空间的话,创建子进程只对会发生修改的数据进行拷贝到内存进行了。

可以让进程管理与内存管理,进行一定的解耦合(使两边的关联变浅)。

地址空间和页表是OS创建并维护的,凡是想使用地址空间和叶表进行映射, 也⼀定要在OS的监管之下来进行访问!!也顺便保护了物理内存中的所有的合法数据 ,包括各个 进程以及内核的相关有效数据!

因为有了虚拟地址空间,那么数据在内存中的存储位置也就没那么重要了。

http://www.lqws.cn/news/545887.html

相关文章:

  • 信息抽取领域关键Benchmark方法:分类体系
  • 不同类型的微型导轨精度降低速度有何差异?
  • 专注搜索引擎优化的专业模板平台
  • 【MySQL进阶】服务器配置与管理——系统变量,选项,状态变量
  • CVE-2015-5531源码分析与漏洞复现(Elasticsearch目录遍历漏洞)
  • C语言高级编程
  • 【日志】Unity游戏实习该怎么准备
  • Unity知识点-Renderer常用材质变量
  • 数据结构之顺序表(C语言版本)
  • 【高并发服务器项目测试报告】
  • 大事件项目记录4-用户接口开发-更新用户基本信息
  • 【论文阅读 | CVPR 2025 |MambaVision:一种混合 Mamba-Transformer 视觉骨干网络】
  • Node.js到底是什么
  • Dockerfile——AI教你学Docker
  • 【Python】实现对LGBT+ rights worldwide (2025)数据集的可视化展示
  • 还在手动部署?用Jenkins+Docker+Git实现自动化CI/CD
  • 使用ant-design-vue 写个适用于web端的条件搜索栏组件,可折叠展开
  • 2025Mybatis最新教程(七)
  • 机器学习中为什么要用混合精度训练
  • 2025.6.27总结
  • HTTP协议中Connection: Keep-Alive和Keep-Alive: timeout=60, max=100的作用
  • SpringMVC系列(四)(请求处理的十个实验(下))
  • 多模态融合相机L3CAM
  • 高斯过程动态规划(GPDP)
  • 免费无广告PDFCreator:虚拟打印软件一键转 PDF/PNG/JPG
  • printf和scanf
  • 问卷调查[bell ring]
  • 全志A733、瑞芯微RK3576与联发科MTK8371场景化应用解析在物联网与智能设备快速迭代的今天,芯片作为硬件核心直接决定了设备的性能边界与应用场景。
  • moduo之tcp客户端TcpClient
  • Webpack 自定义插件开发指南:构建流程详解与实战开发全攻略