【Linux】:程序地址空间
朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux程序地址空间的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!
C 语 言 专 栏:C语言:从入门到精通
数据结构专栏:数据结构
个 人 主 页 :stackY、
C + + 专 栏 :C++
Linux 专 栏 :Linux
目录
1. 程序地址空间分布
2. 基于地址空间,重新理解地址
3. 进程地址空间
3.1 地址空间和区域划分
3.2 为什么要有地址空间?
4. 基于地址空间进行扩展
4.1 每一个进程都有页表
4.2 缺页中断
4.3 进程的独立性
5. 写时拷贝
1. 程序地址空间分布
在C语言阶段就了解过这个图,那么本章来配合代码深入了解一下:
#include <stdio.h> #include <stdlib.h>int un_gval; int init_gval = 100;int main(int argc, char *argv[], char *env[]) {printf("code addr: %p\n", main); // 代码区const char *str = "HelloLinux!";printf("read only char addr: %p\n", str); // 字符常量区printf("init global value addr: %p\n", &init_gval); // 已初始化全局数据区printf("uninit global value addr: %p\n", &un_gval); // 未初始化全局数据区char* heap = (char*)malloc(100);printf("heap addr: %p\n", heap); // 堆区printf("stack addr: %p\n", &str); // 栈区int i = 0;for(i = 0; argv[i]; i++){printf("argv[%d]: %p\n",i, argv[i]); // 命令行参数}for(i = 0; env[i]; i++){printf("env[%d]: %p\n",i, env[i]); // 环境变量}return 0; }
使用代码将对应区域的地址打印出来可以发现于图片完全一致。
① 在程序地址空间中的堆区是向上增长的,栈区是向下增长的,通常也叫做堆栈相对而生。
② 我们定义的任何类型(栈区中)都是整体向下开辟,使用时局部向上使用。
③ 在栈中定义的int类型变量是4个字节,我们要访问时,需要通过它的起始地址再配合它的类型大小进行访问,变量类型大小就相当于起始地址的偏移量,访问的形式就是起始地址 + 偏移量。
④ static修饰局部变量本质上就是将局部变量的地址放到了全局区(全局变量)。
2. 基于地址空间,重新理解地址
在之前的进程创建与进程fork本质章节中遗留了一个问题:如何理解同一个变量会有两个不同的指?
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h>int g_val = 100;int main() {pid_t id = fork();if(id == 0){//childint cnt = 5;while(1){printf("child, Pid: %d, Ppid: %d, g_val: %d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);sleep(1);if(cnt == 0){g_val=200;printf("child change g_val: 100->200\n");}cnt--;}}else{//fatherwhile(1){printf("father, Pid: %d, Ppid: %d, g_val: %d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);sleep(1);}}return 0; }
可以看到具有相同的地址同一个变量居然会有两个值,那么这也就证明了我们C/C++中观察到的地址并不是物理地址,我们平时用到的地址都是虚拟地址/线性地址。
3. 进程地址空间
前面提到的虚拟地址也叫做进程的地址空间,它属于进程PCB中的一个字段,每一个进程在运行之后,都会有一个进程地址空间。
现在就来一步一步解释为什么同一个地址的变量会有两种值:
① 我们定义的全局变量g_val在已初始化全局数据区,里面保存的是该变量的起始地址,进程地址空间不存储数据,它是虚拟地址,那么就要有需要有真正存储数据的地址--物理地址。
② 数据存储在物理地址中,需要通过一种类似于hash的映射关系由虚拟到物理的转化,这种方式在这里叫做--页表,通过页表可以完成由虚拟地址映射到物理地址。
③ 父进程创建子进程的时候需要以自己的PCB为模版来构建子进程的PCB,所以父进程中的全局变量g_val的虚拟地址在子进程的进程地址空间中也会有,同样的,子进程的页表也需要按照父进程为模版构建,所以虚拟到物理的转化关系也有了。
④ 此时,子进程的虚拟地址到物理地址的转化之后也指向了同一块物理地址,当检测到子进程要修改这个变量时,OS会先以写时拷贝的方式在物理地址中重新找一块空间,拷贝原来的数据到新的空间,并将子进程页表中的映射关系随之改变,然后就可以随意的修改变量。
⑤ 当子进程修改完变量的值之后,我们再查看时就会发现同一个地址(虚拟地址)的变量会有两个值。
3.1 地址空间和区域划分
先来了解一下空间的概念(以32位机器为例),在之前C语言的指针阶段就提到,计算机只认识二进制,那么二进制的0或1表示的就是有无的意思,那么在计算机里面的0或1表示的就是是否有电频,32位机器中存在会有32根地址总线,每一根地址线表示的情况都会两种,所以32根地址线一共会有2^32种情况,我们访问数据是以byte为单位,所以它的总大小换算一下就是2^32byte = 4GB大小的空间。
地址空间
假设一个OS的内存一共有4GB的空间大小,在我们运行程序的时候,OS会管理许多的进程,那么进程被调度是需要内存空间的,所以呢,OS就会虚拟的给每一个进程分配OS仅有的4GB的内存空间,那么在OS管理下的所有的进程都会认为自己将来会有4GB的内存空间,简单的说就是OS给每一个进程画了一张饼,那么这张饼就叫做虚拟地址空间(地址空间)。
区域划分
通过一个小故事来理解区域划分:
在某小学,小胖和小花是同桌,共同使用一个长度位100cm的桌子,由于小胖的不注意卫生,遭到了小花的嫌弃,所以呢,小花就提出不再共同使用这张桌子,而是在桌子的中间画一条线,他两每一个用一半,这条线也被我们亲切的称为38线,所以画38线的本质就是对空间进行区域划分
区域调整
还是小胖和小花的这个例子,再画完38线之后呢,小胖和小花愉快的度过了一段时间,但是还是因为小胖的不自觉,经常把自己的垃圾放在小花的那一块,这就让小花很不能忍受,再加上小花实力在小胖之上,所以直接将小胖的区域再次压缩,从之前的五五分直接变成了四六分,对小胖的区域压缩的行为就叫做区域调整。
代码简述
对小胖和小花的这个行为使用计算机语言简单的描述就是:
地址空间也要被管理!
在OS中会有许多的进程,每一个进程都有对应的地址空间,在系统中,一定要对地址空间做管理,防止地址空间的混淆。根据管理的本质:先描述,再组织。
在Linux中,这个进程/虚拟地址空间的东西叫做:struct mm_struct:
它是进程PCB中的一个字段,在PCB中是通过struct mm_struct *mm指向的一个结构化字段。
得出的结论:地址空间最终是一个内核的数据结构对象!就是一个内核结构体,所以我们看到的地址叫做虚拟地址。
3.2 为什么要有地址空间?
1. 地址空间固定的存储结构,可以让进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间 + 页表将乱序的内存数据变成有序并分门别类的规划好。
在我们的计算机中存在许多的程序,那么当程序要运行就要被加载到内存中,OS就要在内存中给进程分配空间,此时的进程的代码和数据会在内存中杂乱的分布,没有顺序,这使得PCB在寻找自己的代码和数据时非常麻烦,地址空间恰好解决了这一点。
2. 地址空间配合页表可以很好的进行进程访问的内存安全检查。
在页表中还存在一个字段,它表示的是访问权限的字段,有的是只读,有的是只写,有的是读写,就比如常量字符串只允许读,不允许修改。地址空间就起到了一个控制检查的作用。
3. 将进程管理和内存管理解耦
由PCB到虚拟地址的提取以及保存的这一过程是属于进程管理的,从内存到物理地址的提取与保存这一过程是属于内存管理的,两者互不影响!
4. 基于地址空间进行扩展
4.1 每一个进程都有页表
在CPU内部有一个寄存器叫做:CR3寄存器,它主要是保存当前进程的页表地址。
在之前的进程切换章节我们了解到,进程要被CPU调度,进程在CPU内运行形成的临时数据叫做进程的硬件上下文,那么页表由虚拟到物理的转化也是属于数据,那么CP3寄存器的数据也叫做该进程的硬件上下文,当进程切换的时候,会将进程的硬件上下文数据从寄存器剥离下来,保存在自己的PCB中,那么每一个进程都要这么做,所以每一个进程都有自己独立的页表。
4.2 缺页中断
页表中的虚拟地址可能有很多,但是物理地址可能还没有分配好,所以再继续访问的时候发现物理地址没有分配好,此时OS就会暂停访问,然后在物理地址中开辟空间,并且修改页表,然后继续执行访问,这个操作叫做缺页中断。
页表中还存在一个字段,它表示的是该地址是否分配或者是否有内容。
4.3 进程的独立性
虚拟地址有很多个,有可能相同,也有可能不同,多个进程通过页表由虚拟地址映射到同一块内存,这些个虚拟地址很可能相同,也有可能不同,通过各自的页表的映射关系之后,所映射的物理地址是完全不一样的,所以即使两个相同虚拟地址的进程,其中一个挂掉了,也不会影响另外一个。
通过页表,让进程映射到不同的物理内存,从而体现了进程具有独立性!
5. 写时拷贝
在前面说到过当子进程写入的时候,OS会发生写时拷贝,重新开辟一块空间给子进程,那么这个写时拷贝中间还存在许多细节:
1. 当父进程形成子进程的时候,子进程开始写入,那么OS会在何时发生写时拷贝?或者说是在某一时机发生写时拷贝?
当父进程创建子进程的时候,首先将自己的页表读写权限改为只读,然后再创建子进程,但是这个过程用户并不知道,当用户进行写入时,会因为页表转化的权限问题而出错,此时,操作系统就会介入,从而触发重新申请内存的拷贝内容的策略机制,这个就叫做写时拷贝。
2. 反正都是要写入,只重新开辟空间就好了,为什么要拷贝原来的内容呢?
我们写入的操作不一定要把原始数据全部修改,如果不拷贝原始数据,然后写入操作,会导致原始数据的丢失以及不完整。
朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!
相关文章:

【Linux】:程序地址空间
朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux程序地址空间的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从…...
c++ 学习面试之路
引用与指针有什么区别? 指针和引用都是地址的概念,指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。 程序为指针变量分配内存区域,而不为引用分配内存区域。 指针使用时要在前加 * ,引…...
Linux文件结构
与Windows下的文件组织结构不同,Linux不使用磁盘分区符号来访问文件系统,而是将整个文件系统表示成树状结构,Linux系统每增加一个文件系统都会将其加入到这个树中。 操作系统文件结构的开始只有一个单独的顶级目录结构,叫做根目录…...

【简单介绍下Memcached】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...

字符串和正则表达式踩坑
// 中石化加油卡号格式:以 100011 开头共19位public static final String ZHONGSHIYOU_OIL_CARD_PATTERN "^100011\\d{13}$";// 中石油加油卡号格式:以90、95、70开头共16位public static final String ZHONGYOU_OIL_CARD_PATTERN "^(9…...

LLM4Decompile——专门用于反编译的大规模语言模型
概述 论文地址:https://arxiv.org/abs/2403.05286 反编译是一种将已编译的机器语言或字节码转换回原始高级编程语言的技术。该技术用于分析软件的内部工作原理,尤其是在没有源代码的情况下;Ghidra 和 IDA Pro 等专用工具已经开发出来&#…...

关于Web开发的详细介绍
目录 一、什么是Web? 二、Web网站的工作流程和开发模式 (1)简单介绍 (2)工作流程 1、第一步 2、第二步 (3)Web网站的开发模式 1、前后端分离开发模式 编辑2、混合开发模式 三、开发W…...

G1 垃圾收集器
从 JDK1.9 开始默认 G1,应用在多处理器和大容量内存环境中。 基础概念 Region G1 给整一块Heap内存区域均匀等分了N个 Region,N 默认情况下是 2048。 Region的大小只能是1M、2M、4M、8M、16M或32M (1-32M,并且为2的指数),比如-Xmx16g -Xms…...
Linux Ubuntu 20.04.06 安装Onboard虚拟键盘教程
目录 一、在线安装 二、源码安装 三、包安装 四、设置 五、禁用系统键盘 一、在线安装 sudo apt-get update #更新软件源 sudo apt-get install onboard #安装Onboard sudo apt-get purge onboard # 卸载 安装后,如果在终端使用命令:onboard 启…...

简介空间复杂度
我们承接上一篇博客。我们写了时间复杂度之后,我们就要来介绍一下另一个相关复杂度了。空间复杂度。我觉得大家应该对空间复杂度认识可能比较少一些。我就是这样,我很少看见题目中有明确要求过空间复杂度的。但确实有这个是我们不可忽视的,所…...

windows server2016搭建AD域服务器
文章目录 一、背景二、搭建AD域服务器步骤三、生成可供java程序使用的keystore文件四、导出某用户的keytab文件五、主机配置hosts文件六、主机确认是否能ping通本人其他相关文章链接 一、背景 亲测可用,之前搜索了很多博客,啥样的都有,就是不介绍报错以…...

android deep links即scheme uri跳转以及googlePlay跳转配置
对于googlePlay的Custom URL就是googlePlay上APP网址: https://play.google.com/store/apps/details?idcom.yourapp如果是国内一些应用,则考虑market://包名等方式,自行百度。 对于Android URI Scheme: 首先需要在Manifest xm…...

QT5.14.2与Mysql8.0.16配置笔记
1、前言 我的QT版本为 qt-opensource-windows-x86-5.14.2。这是QT官方能提供的自带安装包的最近版本,更新的版本需要自己编译源代码,可点击此链接进行下载:Index of /archive/qt/5.14/5.14.2,选择下载 qt-opensource-windows-x86…...

判断是否为完全二叉树
目录 分析 分析 1.完全二叉树的概念:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。 2.思路:可以采…...

【笔记】记一次redis将从节点变成主节点 主节点变成从节点
1.连上虚拟机centos7 2.打开finalshell连接虚拟机 将从节点变为主节点 输出redis-cli -p 要变成主节点的从节点 -a此从节点的密码 输入 replicaof no one 查看端口状态 info replication 总结: redis-cli -p 端口号 -a 密码 replicaof no one info replicati…...
解析Java中1000个常用类:DoubleSummaryStatistics类,你学会了吗?
在线工具站 推荐一个程序员在线工具站:程序员常用工具(http://cxytools.com),有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具,效率加倍嘎嘎好用。程序员资料站 推荐一个程序员编程资料站:程序员的成长之路(http://cxyroad.com),收录了一些列的技术教程…...

WAIC热点聚焦|新质生产力与低空经济
WAIC热点聚焦|新质生产力与低空经济 概览 # WAIC热点聚焦 | 新质生产力与低空经济## 1. 新质生产力定义与特点 - 新质生产力是在新的经济社会发展阶段中形成的,具有变革性和高增长潜力的生产能力。## 2. 低空经济概念与构成 ### 2.1 低空经济定义 - 低空经济是依托…...

Docker部署ETCD 3.5.14(保姆级图文教程)
系列文章目录 Docker部署Nginx 1.21.5(保姆级图文教程) Docker部署MySQL 8.3.0(保姆级图文教程) Docker部署ETCD 3.5.14(保姆级图文教程) 文章目录 一、环境二、拉取镜像2.1 查找 Docker Hub 上的 ETCD 镜像…...

2024年7月6日 (周六) 叶子游戏新闻
自动电脑内部录音器AutoAudioRecorder: 是一款免费的自动音频录制软件,可直接将电脑内部所有的声音录制成 mp3/wav 文件,包括音乐、游戏直播、网络会议、聊天通话等音频源。 卸载工具 HiBitUninstaller: Windows上的软件卸载工具 《不羁联盟》制作人&…...
python爬虫入门(二)之Requests库
一、储备篇 1、requests库让我们可以通过python代码去构建和发送HTTP请求 2、第三方库,要先安装 python终端,输入pip install requests successfully installed:安装成功 requirement already satisfied: 说明已经安装过,无需…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...

【java面试】微服务篇
【java面试】微服务篇 一、总体框架二、Springcloud(一)Springcloud五大组件(二)服务注册和发现1、Eureka2、Nacos (三)负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...