【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的端口号是否开放 【关于路…...
ChIP-seq 分析:数据比对(3)
读取 reads(二者含义相同,下文不做区分)1. ChIPseq reads 比对 在评估读取质量和我们应用的任何读取过滤之后,我们将希望将我们的读取与基因组对齐,以便识别任何基因组位置显示比对读取高于背景的富集。 由于 ChIPseq…...
并非从0开始的c++之旅 day2
并非从0开始的c之旅 day2一、变量1、 变量名的本质二、程序的内存分区模型1、内存分区运行之前运行之后三、栈区注意事项四、堆区1、堆区使用2、堆区注意事项五、全局变量静态变量1、静态变量2、全局变量六、常量1、全局const常量2、局部const常量七、字符串常量一、变量 既能…...
Linux进阶(Shell编程学习一)
由于shell脚本在java项目运维方面极其重要,比如服务的启动脚本,日志的分割脚本,文件的管理脚本大多都是shell脚本去实现的。所以作为java开发者懂linux的基本命令,会基本的shell编程是必要的。 Shell 是一个用 C 语言编写的程序&…...
sql 优化
sql 优化1. mysql 基础架构1.1 mysql 的组成2. mysql 存储引擎2.1MyISAM2.2 InnoDB2.3 MyISAM 和 InnoDB 的对比3. mysql 索引3.1 Hash 索引3.2 B-Tree 索引3.3 BTree 索引3.4 R-Tree 索引3.5 Full-Text 索引4. sql 优化4.1 避免 select *4.2 避免在where子句中使用or来连接条件…...
第7篇:Java的学习路径
目录 1、Java学习主要内容概览 1.1 Java基础 1.2 数据库技术 1.3 动态网页技术 1.4中间件技术...
对抗生成网络GAN系列——Spectral Normalization原理详解及源码解析
🍊作者简介:秃头小苏,致力于用最通俗的语言描述问题 🍊专栏推荐:深度学习网络原理与实战 🍊近期目标:写好专栏的每一篇文章 🍊支持小苏:点赞👍🏼、…...
Solon2 开发之插件,一、插件
Solon Plugin 是框架的核心接口,简称“插件”。其本质是一个“生命周期”接口。它可让一个组件类参与程序的生命周期过程(这块看下:《应用启动过程与完整生命周期》): FunctionalInterface public interface Plugin {…...
使用nvm管理node
nvm介紹 node的版本管理器,可以方便地安装&切换不同版本的node 我们在工作中,可以会有老版本的node的项目需要维护,也可能有新版本的node的项目需要开发,如果只有一个node版本的话将会很麻烦,nvm可以解决我们的难点…...
Linux
第一章 Linux 1.1 计算机硬件软件体系 冯诺依曼 (数学家,计算机之父) 冯诺依曼体系 计算机的指令和数据都是二进制存储,并且存放到一起程序和指令都是顺序执行的计算机硬件由输入,输出,存储,运算器与控制器组成 输入设备 比如:键盘,鼠标等. 输出设备 打印机输出࿰…...
GB28181-2022注册注销基本要求、注册重定向解读和技术实现
规范解读GB28181-2022注册、注销基本要求相对GB28181-2016版本,做了一定的调整,新调整的部分如下:——更改了注册和注销基本要求(见 9.1.1,2016 年版的 9.1.1)。1.增加对NAT模式网络传输要求,宜…...
营销导向网站建设/宁波优化seo软件公司
1、安装过程请点击移步 postcss-pxtorem官网解释点击进入 2、这里只修改了1中的如下部分 postCssPxToRem({// rootValue: 37.5, // 设计图最大宽度除以10 //比如750的宽就写成75 我这边是1125的宽rootValue ({ file }) {// 如果是 Vant 的样式就按照 37.5 处理转换// 如果…...
坪地网站建设/推广方案模板
战神引擎架设正常游戏怎么不开门?战神引擎正常搭建游戏成功后怎么会打不开门?这个怎么解决,跟着idc02大肥羊往下看战神引擎的问世,让传奇手游正式进入平民化,在本站就有一大把的手游服务端,但目前手游和端游…...
wordpress动漫小人2019/如何在百度打广告
iphone如何投屏到mac上?相信小伙伴们一定很好奇,下面小编为大家带来了iphone投屏到mac方法教程说明,感兴趣的小伙伴赶紧跟着小编一起来看看吧。iphone投屏到mac教程:iphone和mac的使用人群越来越多,其中投屏功能却难倒了大部分新手用户,许多…...
做个简单的网站多少钱/今天株洲最新消息
小学英语五年级优秀评课稿范文汤教授执教的是pep Book 5 Unit4 what can you do 的第二课时。这是一节对话新培训,采用多媒体进行教学。对话教学一直是我们同学开公开课时回避的一个内容,因为相对而言,单词教学更容易操作。汤老师今天尝试着上…...
政府网站建设参考书/网络营销的公司有哪些
转眼间学习和使用C已经有近10个年头了,开始学习的时候走了不少的弯路,今天有些时间,希望写下这篇文章并且对开始学习C的朋友有些帮助。当然我首先需要说明的是,这篇文章是根据本人的感受写的,可能不同的人有不同的观点…...
生日快乐软件制作app/上海百度提升优化
今天在转换一个文件时iconv() 老是返回 -1, 提示编码转换失败。 一共 30 多个文件, 原编码都是一样的,为什么有的转换会失败,返回 -1呢? 网上搜索了一下, 找到一个随加参数: //IGNORE 说是 iconv_open() 的…...