Linux->进程地址空间
目录
前言:
1. 程序地址空间回顾
2. 进程空间是什么
3. 进程地址空间与内存
4. 进程地址空间和内存的关联
5. 为什么要有进程地址空间
前言:
我们在平时学习的过程当中总是听到栈、堆、代码段等等储存空间,但是这些东西到底是什么,我们又怎么理解呢?本篇就为大家解惑。
1. 程序地址空间回顾

相信大伙们对于这张图片还是很熟悉吧,特别是对于堆和栈这一片区域,堆向上延伸,栈向下扩展,基本上是共同使用这一片空间。这片空间这样使用主要是服务于我们的动态库。
如果大伙想要验证这一张图片的正确性,可以自己验证一下,低地址代码地址长度更小,高地址更大。代码如下:
#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>int global_uval; //全局变量未初始化
int global_val = 1; //全局变量初始化int main(int argc, char* argv[],char* envp[])
{const char* point = "hello world"; //栈内指针和常量区字符串char* heap = (char*)malloc(100); //栈内指针和堆区空间printf("代码区起始地址: %p\n", main);printf("常量区字符串: %p\n", point);printf("全局变量初始化: %p\n", &global_val);printf("全局变量未初始化: %p\n", &global_uval);printf("堆区地址: %p\n", heap);printf("栈区地址: %p\n", &point);printf("命令行参数: %p\n", argv[0]);printf("环境变量: %p\n", envp[0]);return 0;
}
Linux下可以看到地址空间的变化逐渐增加,也正好对应了我们的地址空间的图。

但是同样的代码在vs下却运行出了不同的结果,如下:

仔细看vs下运行堆区地址和栈区地址大小与Linux下运行相反,具体原因我也不是很清楚,博主在这里也疑惑了半天,估计是vs悄悄的改了一些实现方法。
2. 进程空间是什么
看了上面的解释也没有理解,啥是进程地址空间?博主知道你很急,但是你先别急,什么?非要看?好吧,请看下方结论:
进程地址空间不是真实物理地址而是虚拟地址
知道了吗?没看懂是不?这就对了,还是等我徐徐道来。
请先看下方代码:
利用fork创建了一个子进程,count用于计数,当循环走过两次时,a被修改为100
1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 5 int main()6 {7 int a = 100;8 int count = 0;9 pid_t res = fork(); //创建一个子进程 10 11 while(1)12 {13 if(res == 0)14 {15 printf("我是一个子进程,a = %d, &a = %p\n",a,&a);16 sleep(1);17 }18 else{19 if(count == 2)20 a = 200;21 printf("我是一个父进程,a = %d, &a = %p\n",a,&a);22 count++;23 sleep(1);24 25 }26 }27 28 29 return 0;30 }
运行结果如下:

上图中大家有发现什么不对的地方吗?首先,变量a定义在两个进程分流之前,那么父子进程应该拿到的是同一块数据,通过查看地址,再对应值,确实如此,但继续往下看,当计数两次之后,我们将父进程的a修改了,父进程的a也确实被更改了值,但是,父子进程拿到了同一块数据,为什么子进程的a没有被改变呢?更离谱的是它们两个的地址竟然一模一样,这合理吗?这很不合理。
根据我们学习语言所知,同一块空间只能只能存一个数据,这里竟然存了两个数据,那么根据理论而言,我们能够分析出,我们获取到的地址不可能是真实的物理地址,而是虚拟地址。
但是根据我们学习的计算机体系结构可以知道,任何一个磁盘上的内容想要跑到CPU中被运行,必须经过被载入内存这一过程,但是上面又显示我们拿到的不是真实的物理地址,就不可能是再内存当中的,难道是我们的冯诺依曼体系在这里出错了?很明显不可能,所以只有一种可能,那就是我们在程序中获取到的地址与我们的内存之间有联系。
3. 进程地址空间与内存
这一部分有些抽象,所以我先为大家讲一个故事吧。(这个故事是博主去别人哪里copy的,博主认为十分生动形象)。
咱们在学校总是经历过划分三八线这种事情吧。就是给桌子划分区域,假设有小明和小妞两个人,他们有自己的区域,不能越过区域,到别人的领域去。如下:

我们可以想一下,他们是怎么区分他们自己的区域的呢?不就是记录自己区域的起始结束位置嘛,也就是说小妞想要表示自己的位置,那就是如下:
struct xiaoniu_area{
size_t begin = 0;
size_t end = 50;
};
小明也是同理,也就是说他们自己知道这一片空间的所属就行,不过呢,小明和小妞关系很好,可以亲亲抱抱的那种,所以这一条三八线也就是当作一个提醒罢了。所以呢,小妞有一天要画画,她需要更大的位置操作,那么她就向小明说,我想要多一点点位置,之后还你,小明说:“反正现在我也不用,你用吧”。这样小妞的位置就变为了如下:
struct xiaoniu_area{
size_t begin = 0;
size_t end = 70;
};
那么,相对的小明的位置就被缩减了,图如下:

看到上面的图,大伙有没有将我们的地址分配图联系起来呢?我们的进程地址也就是通过这个方式表示的,就像是我们的栈、堆等等,它的位置都是能够被调整的。
在Linux当中有一个struct mm_struct这样的结构体,用于表示各个区所对应的位置。如下:
struct mm_struct
{//代码区
unsigned long code_start;
unsigned long code_end;//堆区
unsigned long heap_start;
unsigned long heap_end;
//栈区
unsigned long stack_start;
unsigned long stack_end;
}
也就相当于整个进程地址空间的使用被这样一个结构体划分的明明白白。
但是这又跟我们的内存有什么联系呢?别着急,接着看。
我先给出结论:
每个进程都可以独占整个内存空间
怎么理解上面这句话呢?咋一看是不是感觉很奇怪?仔细一看更奇怪了是不?
我还是举一个例子。
有一个10亿家产的富豪,他有2个私生子,这两个私生子不知道彼此的存在,此时呢,富豪对两个孩子分别说了,只要我寄了,这10亿就是你的了,所以呢,这两个还是都认为这10亿自己稳得了。这时,大儿子说,老爸,我想要20万买个车上班,富豪说,行,好好加油,之后小儿子又说,老爸,给我5个亿,我要单开一家公司,做大做强,富豪转身就抽出了七匹狼,你要皮带不要?小儿子就跑了。但是对于大儿子和小儿子来说,他们认为这10还是自己得吗?肯定是啊。
这里的富豪就是操作系统,10亿就是内存,而两个儿子就是进程,他们可以向操作系统申请空间,操作系统也可以拒绝他们的请求,但是他们能得到的空间是内存大小的空间。这也就表明了,每一个进程都认为自己是可以独占内存的,但是操作系统是能够自己判断是否给你这么多。
4. 进程地址空间和内存的关联
进程地址空间通过页表和内存关联起来。
什么是页表?
页表就是:进程将自己的代码和数据首先放在虚拟地址空间的对应的区域,在这其中会有一种表结构,叫做页表,页表的核心工作就是完成虚拟地址到物理地址之间的映射,最终我们的可执行程序的代码和数据可以加载到物理内存的任意位置,因为最终只需要建立代码和数据与物理内存之间的映射关系,就可以通过虚拟地址找到物理内存的对应地址。
咱们可以想到每一个进程都是独立的,父子进程也是不例外的,那么每一个进程也都有属于知道的页表去对应真实的内存。看到这里,想必大家也能回到之前父子进程不同值,地址相同得问题了吧。那就是父子进程得虚拟地址相同,但是这个虚拟地址存在于两张页表当中,这两个页表通过相同的虚拟地址却映射了两个内存空间。
那么,这两个映射不会映射到一个空间吗?
不会,还记得我之前讲的mm_struct结构体,内存将这些空间用结构体划分了起来,并且划分之后还会标识这片区域已经被某个进程使用了,那么页表就知道了这篇空间不能使用了,它就会换一片区域去映射。如下:

看到上面的图,我们可以认为父子进程是完全独立的吗?
其实不能,因为我们的操作系统是十分会节省空间的,也就是当父进程或子进程的数据没有被改变的时候,两个页表指向的真实空间也是相同的,只有在数据发生改变的时候操作系统会为这一个空间开辟一个新的空间,然后对应给页表,但是页表的虚拟内存没有改变。这一过程被称为写时拷贝。
5. 为什么要有进程地址空间
我们使用malloc时,操作系统在我们申请内存的时候并没有直接的给我们那一片地址,但是这一片空间并不能被其他的进程使用,该片地址会处于一个闲置状态。这篇地址不能使用是这一个进程不能使用,而是其它进程可以使用,但是操作系统会保证数据不会冲突。
也就是说,我们能够获取到虚拟地址,但是我们没有在意到底有没有实际的物理地址在哪里,反正我们要使用的时候,操作系统能给我们就是了。所以这样做就让我们的进程管理和内存管理之间解耦了。
还有就是当操作系统要加载一个很大的程序,比如32个G,这样一个程序一定是不可能全部运行起来的,所以操作系统会慢慢的加载,没有被使用的部分就被先睡眠起来了。我们唯一的感受也就是程序变慢了。
并且,通过进程地址空间,我们操作系统能够清楚地知道你写的进程是否正确,有没有越界?如果越界了,那么操作系统就会让你的进程崩溃,因为你的指针或者其它数据指向了其它进程的区域,保证了进程之间的独立性和安全性。
以上就是我对进程地址空间的全部理解咯,有问题请帮忙指出啦,博主也是努力进步当中,哈哈。
相关文章:
Linux->进程地址空间
目录 前言: 1. 程序地址空间回顾 2. 进程空间是什么 3. 进程地址空间与内存 4. 进程地址空间和内存的关联 5. 为什么要有进程地址空间 前言: 我们在平时学习的过程当中总是听到栈、堆、代码段等等储存空间,但是这些东西到底是什么&…...
【奶奶看了也不会】AI绘画 Mac安装stable-diffusion-webui绘制AI妹子保姆级教程
1.作品图 2.准备工作 目前网上能搜到的stable-diffusion-webui的安装教程都是Window和Mac M1芯片的,而对于因特尔芯片的文章少之又少,这就导致我们还在用老Intel 芯片的Mac本,看着别人生成美女图片只能眼馋。所以小卷这周末折腾了一天&#…...
基于stm32电梯管理系统设计
基于stm32电梯管理系统设计这里记录一下以前自己做的嵌入式课程设计,报告中的图片和文字太多了,全部一个一个把搬过来太麻烦了,需要完整文本和代码自行q我963160156,也可在微信公众号 *高级嵌入式软件* 里回复 *电梯* 查看完整版文章摘要关键…...
Spring中的FactoryBean 和 BeanFactory、BeanPostProcessor 和BeanFactoryPostProcessor解析
文章目录FactoryBean 和 BeanFactory后置处理器BeanPostProcessor 和 BeanFactoryPostProcessorBeanPostProcessorBeanFactoryPostProcessorFactoryBean 和 BeanFactory BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的…...
【C++从入门到放弃】类和对象(上)
🧑💻作者: 情话0.0 📝专栏:《C从入门到放弃》 👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢! 类和对…...
什么牌子的蓝牙耳机便宜好用?四款高品质蓝牙耳机推荐
随着时代的发展,蓝牙耳机的使用频率越来越高,不少人外出时除了带手机外,蓝牙耳机也成为了外出必备的数码产品之一。现在的蓝牙耳机品牌众多,什么牌子的蓝牙耳机便宜好用?下面,我来给大家推荐四款高品质的蓝…...
eddsa 算法
信息安全课程设计:eddsa 算法 一、项目要求 使用 C 语言开发;可以实现公私钥生成、签名、认证;只需要手动输入明文,代码会自动生成公私钥、签名、认证;记录公私钥生成、签名、认证的时间;在 VS 上运行&am…...
Xcode Developer Document 开发者文档
总目录 iOS开发笔记目录 从一无所知到入门 文章目录IntroDeveloper Documentation 打开方式菜单栏点击 | 快捷键方式另一种打开方式Intro 2016年我在学校学Java的时候,要查某个Java类/方法的用法还得自己手动下载一种.chm格式的开发文档文件,…...
IntelliJ插件开发教程之新建项目
JetBrains公司系列产品IDEA、WebStrom、PyCharm、CLion、GoLand等都是基于IntelliJ Platform开发而成,掌握IntelliJ插件开发技能便能拥有提升开发效率的终极武器。本教程Demo源码请微信公众号“开发效率”进行获取。阅读原文如果您是JetBrains产品的用户,…...
解决SpringBoot中@RequestBody不能和Multipart同时传递的问题
问题描述 今天在做文件上传的时候,遇到了这么一个错误日志: Resolved[org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘multipart/form-data;boundary--------------------------771899451541318130280588;charsetUTF-8’…...
【华为OD机试模拟题】用 C++ 实现 - 统计匹配的二元组个数(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明统计匹配的二元组个数题目输入输出描述示例一输入输出说明示例二输入输出说明备注Code使用说明 参加华为od机试,一定要注意不要完全背诵代码&...
Vuex 面试题总结 的历史汇总!
一.vuex是什么?怎么使用?哪种功能场景使用它? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简单来说就是:应用…...
Redis缓存更新策略与缓存穿透、雪崩等问题的解决
文章目录一、缓存更新策略1、三种策略2、策略选择3、主动更新的方案二、缓存存在的问题1、缓存穿透2、缓存雪崩3、缓存击穿三、解决缓存问题1、自定义分布式锁2、解决缓存穿透问题3、解决缓存击穿问题一、缓存更新策略 1、三种策略 内存淘汰:redis自带的内存淘汰机…...
OSI和TCP/IP网络模型细讲
文章目录一、OSI七层参考模型二、TCP/IP体系结构三、TCP/IP参考模型四、沙漏计时器形状的TCP/IP协议族五、两种国际标准对比相似之处不同之处一、OSI七层参考模型 OSI参考模型共分为7层,低三层面向通信,可用软硬件实现;高三层面向信息处理&am…...
【正点原子FPGA连载】第十九章FreeRtos Hello World实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十九章FreeRto…...
php mysql高校田径运动会成绩管理系统
第一章 引言 1 1.1 选题背景 1 1.2 编写目的 2 1.3 目标 2 1.4 功能需求 3 第二章 开发工具介绍 4 2.1 PHP 4 2.2 APACHE 5 2.3 MYSQL数据库 5 2.4 运行环境 WINDOWS XP 6 2.5 XAMPP 6 2.6 DREAMWEAVE8 6 2.7 EDITPLUS 7 第三章 需求…...
scrum敏捷项目管理软件三款
Leangoo领歌Leangoo是国产的一个项目管理软件,www.leangoo.com , 专门的Scrum敏捷开发工具,看板的管理方式,高度可视化。它支持敏捷开发全流程。从产品路线图-需求-迭代-缺陷-测试-上线。燃尽图,工作量,迭代…...
【项目设计】高并发内存池(二)[高并发内存池整体框架设计|threadcache]
🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…...
西电编译原理期末核心考点汇总(期末真题+相关知识点)
文章目录前言一、正规式1.1 相关知识点1.1.1 正规式定义1.1.2 辅助定义1.2 历年真题二、二义文法2.1 相关知识点2.1.1 二义性概念2.2 历年考题三、全部短语、直接短语和句柄3.1 相关知识点3.1.1 短语,直接短语和句柄定义3.1.2 短语,直接短语和句柄例题3.…...
追梦之旅【数据结构篇】——详解C语言实现二叉树
详解C语言实现二叉树~😎前言🙌什么是二叉树?二叉树的性质总结:整体实现内容分析💞1.头文件的编写:🙌2.功能文件的编写:🙌1)前序遍历的数值来创建树——递归函…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...
信息系统分析与设计复习
2024试卷 单选题(20) 1、在一个聊天系统(类似ChatGPT)中,属于控制类的是()。 A. 话语者类 B.聊天文字输入界面类 C. 聊天主题辨别类 D. 聊天历史类 解析 B-C-E备选架构中分析类分为边界类、控制类和实体类。 边界…...
