【Linux】程序进程地址空间
程序地址空间
在Linux下,这种地址叫做 虚拟地址, 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理
问:
C/C++程序地址空间是内存吗?
-> 根本就不是内存! 是进程虚拟地址空间
堆栈相对而生,栈向下生长(在栈上的变量先定义的地址更大),堆向上生长
代码区: main函数就在代码区,函数名就是函数的地址
字符常量区:const char* str = "hello world"
str指向的字符串的起始地址在字符常量区,但是str本身是在栈区的,所以str才是字符常量区内的地址, &str是栈区内的地址
#include<stdio.h>
#include<stdlib.h>int g_unval;
int g_val = 100;int main(int argc,char* argv[],char* env[])
{const char* str = "hello world";printf("code addr:%p\n",main);//main函数就在代码区printf("string rdonly addr:%p\n",str);//字符常量区printf("init addr:%p\n",&g_val);//已初始化全局数据区printf("uninit addr:%p\n",&g_unval);//未初始化全局数据//堆区char* heap1 = (char*)malloc(10);char* heap2 = (char*)malloc(10); char* heap3 = (char*)malloc(10); char* heap4 = (char*)malloc(10); printf("heap1 addr:%p\n",heap1); printf("heap2 addr:%p\n",heap2); printf("heap3 addr:%p\n",heap3); printf("heap4 addr:%p\n",heap4); //栈区int a = 10; int b = 20; printf("stack addr:%p\n",&a); printf("stack addr:%p\n",&b); //命令行参数int i = 0for( 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;
}
栈区和堆区的地址值相差很大,说明中间有漏空 堆区和栈区相对而生,栈区:先使用高地址,再使用低地址
栈区是栈区,堆区是堆区 我们平常说的堆栈,实际是栈区
一个奇怪的现象:
我们发现,当数据发生修改的时候,对于同一个变量在父子进程当中,地址是相同的,但是值却是不同, 是什么原因呢?
前面我们已经知道:fork创建子进程时,父子默认情况共享数据,修改数据时,为了维护进程独立性,会发生写时拷贝,所以一个值不变,一个值发生改变, 这个可以理解.但是地址为什么会不变呢?
如果我们是在同一个物理地址处获取的值,那必定值是相同的,而现在在同一个地址处获取到的值却不同,这只能说明我们打印出来的地址绝对不是物理地址
实际上,我们在语言层面上打印出来的地址都不是物理地址,而是虚拟地址,而物理地址用户一概是看不到的,是由操作系统统一进行管理的,所以就算父子进程当中打印出来的全局变量的地址(虚拟地址)相同,但是两个进程当中全局变量的值却是不同的
- 虚拟地址和物理地址之间的转化由操作系统完成
- OS必须负责将 虚拟地址 转化成 物理地址
进程地址空间
我们之前将那张布局图称为程序地址空间实际上是不准确的,那张布局图实际上应该叫做进程地址空间, 进程地址空间本质上是内存中的一种内核数据结构,
-
每个进程都有一个地址空间,操作系统为每一个进程画了一个大饼,它们都认为自己在独占物理内存
-
系统中存在大量进程,需要管理地址空间,那么就需要先描述、再组织
-
进程地址空间本质上在内核中是一个数据类型 ,可以定义具体的进程地址空间变量,在Linux当中进程地址空间具体由结构体mm_struct实现
struct mm_struct {//进程地址空间 };
那我们是怎么使用struct结构体进行区域划分的?各个区域又是如何与物理内存建立关联的, 我们将实体物理内存抽象出一把尺子,上面的刻度相当于虚拟地址(地址空间进行区域划分时,对应的线性位置虚拟地址)
struct mm_struct
{unsigned int code_start; unsigned int code_start; unsigned int init_data_start;unsigned int init_data_end;unsigned int uninit_data_start;unsigned int uninit_data_end;//....unsigned int stack_start;unsigned int stack_end;
};
堆向上增长以及栈向下增长实际就是改变mm_struct当中堆和栈的边界start和end的值
每个进程都认为自己拥有4GB,都认为空间的划分是按照4GB来划分的.虽然这里只有start和end,但这是一个区间概念,每个进程都认为mm_struct
代表的是从0x00000000到0xFFFFFFFF整个内存
那么如何将虚拟地址和物理地址建立映射关系呢?通过查页表(页表+MMU硬件设备)
页表的作用:将虚拟地址转化为物理地址, 在上述图中,页表中表格的左部分是虚拟地址,右部分是物理地址
为什么要这样做:(为什么要有地址空间)
-
1. 通过添加一层软件层,完成有效的对进程操作内存的风险管理(权限管理),本质是为了保护物理内存各个进程的数据安全
-
类似于过年的压岁钱妈妈帮你收着,等你要用的时候,再来问我要,防止你乱花钱.对应到这里,中间层是有利于操作系统管理的,不是不给你,而是管控你的做法是否合适;如果没有中间层(OS),能直接访问物理地址,可能发生非法越界访问
-
const char* str = "hello" str[0] = 'a';//err,不可以修改
类似于:我们所知道的:常量字符串的内容不可以修改,本质上是因为,这里str指针指向的就是虚拟地址,解引用进行写入时,访问虚拟地址,要进行虚拟地址和物理地址的转化,然而OS只给你读®的权限,我们进行写入,进程就会崩溃掉
-
-
2.将内存申请和内存使用在时间上解耦.通过虚拟地址空间,来屏蔽底层申请内存的过程,达到进程读写内存操作和OS进行内存管理进行软件层面上的分离
- 比如我们在堆上申请一大块空间,但是我们可能暂时不会全部使用甚至暂时不用(有了空间,从来没有读写),在OS角度,这部分空间本来是可以给别人立马用的,却被闲置着.于是,OS在当你真的要使用时,再把空间开辟出来,建立映射关系,这叫做基于缺页中断进行物理内存申请.
- 再比如假如物理内存已经100%占满了,而你还要,那么OS执行内存管理算法,把某些进程闲置的空间置换到磁盘上,这样进程照样可以申请到内存.而这些都是我们用户在应用层根本感受不到,换句话说OS做的内存操作是透明的
-
3.站在CPU和应用层的角度,进程统一使用4GB的空间,且每个空间区域的相对位置是比较确定的
- 比如CPU寻找不同进程代码的第一行,如果直接访问物理内存,CPU会比较凌乱.有了虚拟地址空间,CPU能以统一的视角看待物理内存,不同的进程再通过的各自的页表,映射到不同的物理内存.同时,程序的代码和数据可以加载到内存的任意位置,大大减少内存管理的负担
所以我们回到最初的问题:相同的地址会打印出不同的值的原理:
子进程的创建是以父进程为模板的: 每个进程被创建时,其对应的进程控制块(task_struct)和进程地址空间(mm_struct)也会随之被创建,而操作系统可以通过进程的task_struct找到其mm_struct,因为task_struct当中有一个结构体指针存储的mm_struct的地址
当子进程刚刚被创建时,子进程和父进程的数据和代码是共享的,即父子进程的代码和数据通过页表映射到物理内存的同一块空间
只有当父进程或子进程需要修改数据时,才将父进程的数据在内存当中拷贝一份,然后再进行修改, 为了维护进程的独立性,子进程在更改时发生写时拷贝,即为子进程重新开辟一段物理空间,把值拷贝过来,再重新建立虚拟地址到物理地址的映射关系
所以打印的是一样的虚拟地址,而不同的值,是因为在物理内存上本来就是不同的变量.之前说的,父子进程的代码一般是共享的,也就是通过映射到同一段物理空间实现的. 之前说的,所有的只读数据一般可以只有一份,本质不是在语言上,而是在系统上,这样操作系统的维护成本是最低的,不同的虚拟地址映射到相同的物理地址上
例如:C语言时候的:
//str1和str2都指向同一块空间,只读数据一般可以只有一份,操纵系统只维护一份相同的内容
const char* str1 = "hello world";
const char* str2 = "hello world";
printf("%p\n",str1);//00007ff72e9d9000
printf("%p\n",str2);//00007ff72e9d9000
进程和程序有什么区别? 进程要包括描述进程的进程控制块PCB(task_struct)、进程虚拟地址空间(mm_struct)、页表、代码和数据
- 写时拷贝的好处:
进程具有独立性.多进程运行,需要独享各种资源,多进程运行期间互不干扰,不能让子进程的修改影响到父进程
- 为什么不在创建子进程的时候就进行数据的拷贝
子进程不一定会使用父进程的所有数据,并且在子进程不对数据进行写入的情况下,没有必要对数据进行拷贝,我们应该按需分配,在需要修改数据的时候再分配(延时分配),这样可以高效的使用内存空间
所有的只读数据一般可以只有一份,本质不是在语言上,而是在系统上,这样操作系统的维护成本是最低的,不同的虚拟地址映射到相同的物理地址上
- 代码会不会进行写时拷贝
多半情况是不会的,但是代码可以进行写时拷贝
例如在进行进程替换的时候,则需要进行代码的写时拷贝
- 进程地址空间存在意义
1、有了进程地址空间后,就不会有任何系统级别的越界问题存在了.例如进程1不会错误的访问到进程2的物理地址空间,因为你对某一地址空间进行操作之前需要先通过页表映射到物理内存,而页表只会映射属于你的物理内存.总的来说,虚拟地址和页表的配合使用,本质功能就是包含内存
2、有了进程地址空间后,每个进程都认为看得到都是相同的空间范围,包括进程地址空间的构成和内部区域的划分顺序等都是相同的,这样一来我们在编写程序的时候就只需关注虚拟地址,而无需关注数据在物理内存当中实际的存储位置
3、有了进程地址空间后,每个进程都认为自己在独占内存,这样能更好的完成进程的独立性以及合理使用内存空间(当实际需要使用内存空间的时候再在内存进行开辟),并能将进程调度与内存管理进行解耦或分离
相关文章:

【Linux】程序进程地址空间
文章目录程序地址空间进程地址空间程序地址空间 在Linux下,这种地址叫做 虚拟地址, 我们在用C/C语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理 问:C/C程序地址空间是内存吗? -> 根本就不是内存! 是进程虚拟地址空间 堆栈…...

电压放大器在液滴微流控芯片的功能研究中的应用
实验名称:电压放大器在液滴微流控芯片的功能研究中的应用研究方向:微流控生物芯片测试目的:液滴微流控技术能够在微通道内实现液滴生成,精准控制生成液滴的尺寸以及生成频率。结合芯片结构设计和外部控制条件,可以对液…...

Linux操作系统学习(进程地址空间)
文章目录进程地址空间奇怪的现象什么是进程地址空间???虚拟地址是如何与物理内存联系的?页表是什么呢?为什么要有页表和地址空间,让进程直接访问内存不行吗?现象解释进程地址空间 在我们学习其…...
【排序】快速排序实现
目录 一、快速排序是什么? 二、左右指针法 1.实现原理 2.代码如下: 三、挖坑法 1.实现原理 2.代码如下: 四、前后指针法 1.实现原理 2.代码如下: 五、三数取中 1.实现思想 2.代码如下: 3.使用方法 总结…...

YOLOv5/v7 Flask Web 车牌识别 | YOLOv7 + EasyOCR 实现车牌识别
YOLOv7 Flask Web 车牌识别图片效果展示 本篇博文只包含源码以及使用方式,目前不同提供详细开发教程。 YOLOv7 Flask Web 车牌识别视频效果展示 YOLOv7 + EasyOCR 实现车牌识别 什么是Flask? 简介 Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更…...

【Opencv实战】几十年前的Vlog火了:黑白老照片如何上色?这黑科技操作一定要知道,复原度超高,竟美的出奇~(图像修复神级代码)
导语 哈喽大家好呀!我是每天疯狂赶代码的木木子吖~情人节快乐呀! 所有文章完整的素材源码都在👇👇 粉丝白嫖源码福利,请移步至CSDN社区或文末公众hao即可免费。 我们都知道,有很多经典的老照片…...

React源码分析(一)Fiber
前言 本次React源码参考版本为17.0.3。 React架构前世今生 查阅文档了解到, React16.x是个分水岭。 React15及之前 在16之前,React架构大致可以分为两层: Reconciler: 主要职责是对比查找更新前后的变化的组件;R…...

小樽 C++指针—— (壹) 指针变量
(壹) 指针变量 一、指针的概念与定义 二、给指针变量p赋值 三、指针变量的的、-运算 四、无类型指针 五、多重指针 C (壹) 指针变量 小明想把从李华家借来的书——《CCF中学生计算机程序设计》还给李华,但李华不在家,于是把书放到书架第3层的最右边…...

java 代码块 万字详解
概述 : 特点 : 格式 : 情景 : 细节 : 演示 : 英文 : //v,新版编辑器无手动添加目录的功能,PC端阅读建议通过侧边栏进行目录跳转;移动端建议用PC端阅读。😂一、概述 :代码块,也称为初始化块,属于类中的成员&…...

杂项-图片隐写
图片隐写的常见隐写方法: 三基色:RGB(Red Green Blue) 图片文件隐写 1.Firework 使用winhex打开文件时会看到文件头部中包含firework的标识,通过firework可以找到隐藏图片。 使用场景:查看隐写的图片文件…...

【高性价比】初学者入门吉他值得推荐购买的民谣单板吉他品牌—VEAZEN费森吉他
“在未知的世界里,我们是一群不疲不倦的行者,执念于真善美,热衷于事物的极致。我们抽丝剥茧,不断地打败自己,超越自己,我们无所畏惧终将成为巨人。”这是VEAZEN吉他官网首页上很明显的一段话,也…...

2023年浙江交安安全员考试题库及答案
百分百题库提供交安安全员考试试题、交安安全员考试真题、交安安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 50.根据《建设工程安全生产管理条例》第65条规定,施工单位有下列()行…...
【新】华为OD机试 - 跳格子(Python)
跳格子 题目 地上共有 N 个格子,你需要跳完地上所有的格子, 但是格子间是有强依赖关系的,跳完前一个格子后, 后续的格子才会被开启,格子间的依赖关系由多组 steps 数组给出, steps[0] 表示前一个格子, steps[1] 表示 steps[0] 可以开启的格子: 比如 [0,1] 表示从跳完第…...
乡村能做社区团购吗?怎么做?我走访调查后发现机会很大
乡村能做社区团购吗?怎么做?我走访调查后发现机会很大#深度触网 #社区团购 #乡村振兴##乡村旅游##县域经济##市场经济##农文旅产业振兴研究院#乡村旅游能带动农产品加工业、服务业、商贸业等相关联产业的发展 乡村能做社区团购吗?怎么做&…...

态路小课堂丨下一代数据中心100G接口第二篇——SFP-DD封装
100G光模块根据封装模式可分为QSFP28、CXP、CFP、CFP2、FCP4、DSFP和SFP-DD等。态路小课堂之前已经大量介绍了相关内容(。 态路小课堂丨下一代数据中心100G接口——DSFP态路小课堂丨100G解决方案-425G NRZ光模块态路小课堂丨什么是100G QSFP28单波光模块?…...
状态栏和导航栏高度获取
/*** 获取导航栏高度*/public static int getNavigationBarHeight(Context context){int navigationBarHeight 0;int resourceId context.getResources().getIdentifier("navigation_bar_height", "dimen", "android")if (resourceId > 0) {…...
插曲:第一桶金 1w 的来由
因为前天跟同事聊天,发现有个比较严重的认知,就是关于赚钱思维。 同事反馈说工作十来年,却没有接过私活,这里话分两头,有可能私 活钱少,但他给我的理由是:私活太麻烦,有时候不敢接&a…...
中国甲基异丁基甲醇行业头部企业市场占有率及排名调研报告
内容摘要 本文调研和分析全球甲基异丁基甲醇发展现状及未来趋势,核心内容如下: (1)全球市场总体规模,分别按销量和按收入进行了统计分析,历史数据2018-2022年,预测数据2023至2029年。 …...

streamlit自定义组件教程和组件开发环境配置
About create your own component: you can follow this tutorial streamlit tutorial 重要!以下步骤都是在教程的基础上更改的。这个教程做的很棒。 Component development environment configuration: 根据文章 https://streamlit-com…...

Windows CMD常用命令
目录 【打开CMD命令】 【网络测试命令】 ipconfig------查看本机网卡信息 ping------测试网络是否通畅 tracert------追踪路由,也可以用来查看网络连通性 telnet------查看目的主机ip的端口号是否开放 tcping------查看目的主机ip的端口号是否开放 【关于路…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...

【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...