【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. 反正都是要写入,只重新开辟空间就好了,为什么要拷贝原来的内容呢?

我们写入的操作不一定要把原始数据全部修改,如果不拷贝原始数据,然后写入操作,会导致原始数据的丢失以及不完整。

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/371322.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PingCAP 成为全球数据库管理系统市场增速最快的厂商

近日&#xff0c;Gartner 发布的《Market Share Analysis: Database Management Systems, Worldwide, 2023》&#xff08;2024 年 6 月&#xff09;报告显示&#xff1a;“2023 年全球数据库管理系统&#xff08;DBMS&#xff09;市场的增长率为 13.4%&#xff0c;略低于去年的…

LLM4Decompile——专门用于反编译的大规模语言模型

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

学习笔记——动态路由——OSPF聚合(汇总)

十一、OSPF聚合(汇总) 1、路由聚合(汇总) 路由汇总是一种重要的思想&#xff0c;在大型的项目中是必须考虑的一个重点事项。随着网络的规模越来越大&#xff0c;网络中的设备所需维护的路由表项也就会越来越多&#xff0c;路由表的规模也就会逐渐变大&#xff0c;而路由表是需…

【TB作品】51单片机 Proteus仿真 超声波LCD1602ADC0832 身高体重测量仪

00024 超声波LCD1602ADC0832 实验报告&#xff1a;基于51单片机的身高体重测量仪设计 背景介绍 本实验设计并实现了一个基于51单片机的身高体重测量仪。该系统利用超声波传感器测量高度&#xff0c;通过ADC0832模数转换芯片获取重量数据&#xff0c;并使用LCD1602显示屏显示…

系统测试-测试方法学习

目录 &#xff08;1&#xff09;等价类 &#xff08;2&#xff09;边界值 &#xff08;3&#xff09;正交&#xff1a;&#xff08;只用于确定排列组合&#xff0c;不确定具体内容&#xff09; (4)判定表法 &#xff08;5&#xff09;流程分析法 &#xff08;6&#xff0…

Day05-04-持续集成总结

Day05-04-持续集成总结 1. 持续集成2. 代码上线目标项目 1. 持续集成 git 基本使用, 拉取代码,上传代码,分支操作,tag标签 gitlab 用户 用户组 项目 , 备份,https,优化. jenkins 工具平台,运维核心, 自由风格工程,maven风格项目,流水线项目, 流水线(pipeline) mavenpom.xmlta…

uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据@getIndex点击事件获取点击的地区下标和地区名

项目场景&#xff1a; uni-app使用ucharts地图,自定义Tooltip鼠标悬浮显示内容并且根据getIndex点击事件获取点击的地区下标和地区名 例如&#xff1a; 问题描述 官方给的文档有限&#xff0c;需要自己下载地图json数据然后自己渲染和编写鼠标悬浮显示内容以及获取点击地址…

在DevEco运行typeScript代码,全网详细解决执行Set-ExecutionPolicy RemoteSigned报出的错

目录 基本思路 网络推荐 本人实践 如下操作,报错: 基本思路 //在DevEco运行typeScript代码 /** * 1.保证node -v出现版本,若没有,配置环境变量(此电脑-属性-高级系统变量配置-path-粘贴路径);DevEco在local.properties中可看到当前nodejs的路径 * 2.npm install …

【Transformer】transformer模型结构学习笔记

文章目录 1. transformer架构2. transformer子层解析3. transformer注意力机制4. transformer部分释疑 图1 transformer模型架构 图2 transformer主要模块简介 图3 encoder-decoder示意图N6 图4 encoder-decoder子层示意图 1. transformer架构 encoder-decoder框架是一种处理NL…

strcpy,srtcmp,strlen函数漏洞利用

strcpy,srtcmp,strlen函数漏洞利用 strcpy strcpy函数用于将字符串复制到另一个指针指向的空间中&#xff0c;遇到空字符 **b’x\00’**时停止&#xff0c;&#xff1a; 所以可以利用 strcpy不检查缓冲区 的漏洞&#xff08;构造的字符串要以\0结尾&#xff09;&#xff0c;…

Android平台崩溃和 ANR 问题进行符号化解析、解析崩溃日志的内存地址

使用Android Logcat Stacktrace Utility | Android Logcat | 1.2.3 1.设置so库路径 2.打开Stacktrace Utility工具 3.在Original粘贴报错内存地址 4.点击Resolve Stacktraces,就会解析出内存地址 如果是红色,解析失败了,缺少原生so库,可以在第一步添加so库文件再次尝试…

freemarker生成pdf,同时pdf插入页脚,以及数据量大时批量处理

最近公司有个需求&#xff0c;就是想根据一个模板生成一个pdf文档&#xff0c;当即我就想到了freemarker这个远古老东西&#xff0c;毕竟freemarker在模板渲染方面还是非常有优势的。 准备依赖&#xff1a; <dependency><groupId>org.springframework.boot</gr…

【植物大战僵尸杂交版】获取+存档插件

文章目录 一、还记得《植物大战僵尸》吗&#xff1f;二、在哪下载&#xff0c;怎么安装&#xff1f;三、杂交版如何进行存档功能概述 一、还记得《植物大战僵尸》吗&#xff1f; 最近&#xff0c;一款曾经在15年前风靡一时的经典游戏《植物大战僵尸》似乎迎来了它的"文艺复…

EN-SLAM:Implicit Event-RGBD Neural SLAM解读

论文路径&#xff1a;https://arxiv.org/pdf/2311.11013.pdf 目录 1 论文背景 2 论文概述 2.1 神经辐射场&#xff08;NeRF&#xff09; 2.2 事件相机&#xff08;Event Camera&#xff09; 2.3 事件时间聚合优化策略&#xff08;ETA&#xff09; 2.4 可微分的CRF渲染技术…

CosyVoice多语言、音色和情感控制模型,one-shot零样本语音克隆模型本地部署(Win/Mac),通义实验室开源

近日&#xff0c;阿里通义实验室开源了CosyVoice语音模型&#xff0c;它支持自然语音生成&#xff0c;支持多语言、音色和情感控制&#xff0c;在多语言语音生成、零样本语音生成、跨语言声音合成和指令执行能力方面表现卓越。 CosyVoice采用了总共超15万小时的数据训练&#…

学习笔记——动态路由——OSPF(邻接/邻居)

十、OSPF的邻接/邻居 1、OSPF路由器之间的关系 (1)基本介绍 在OSPF网络中&#xff0c;为了交换链路状态信息和路由信息&#xff0c;邻居设备之间首先要建立邻接关系&#xff0c;邻居(Neighbors)关系和邻接(Adjacencies)关系是两个不同的概念。 OSPF路由器的两种关系&#x…

创建线程的五种方式

一.继承Thread ,重写run class MyThread extends Thread{Overridepublic void run() {//这里的内容就是该线程要完成的工作while(true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeExceptio…

【CUDA】

笔者在学习Softmax实现时遇到了一个问题&#xff0c;很多文章直接将softmax的计算分成了五个过程&#xff0c;而没有解释每个过程的含义&#xff0c;尤其是在阅读这篇文章时&#xff0c;作者想计算最基本的softmax的效率&#xff0c;以展示可行的优化空间&#xff1a; 贴一个g…

windows 服务器安装svn服务端、迁移svn

svn服务器版本 因为要把旧svn迁移到新的svn&#xff0c;为了保证迁移后的稳定性&#xff0c;安装包使用的旧服务器的svn服务器版本 VisualSVN-Server-3.6.1-x64.msi 安装 配置仓库路径等 其他没截图的就默认配置下一步即可。安装完成先不要启动 迁移 旧的svn服务器直接把…

软件工程(上)

目录 软件过程模型&#xff08;软件开发模型&#xff09; 瀑布模型 原型模型 V模型 构件组装模型 螺旋模型&#xff08;原型瀑布&#xff09; 基于构件的软件工程&#xff08;CBSE&#xff09; 快速应用开发模型&#xff08;RAD&#xff09; 统一过程&#xff08;UP&a…