当前位置: 首页 > news >正文

【Linux学习】进程地址空间与写时拷贝

文章目录

  • Linux进程内存布局图:
      • 内存布局的验证
  • 进程地址空间
    • 写时拷贝


Linux进程内存布局图:

地址空间的范围,在32位机器上是2^32比特位,也就是[0,4G]。
在这里插入图片描述

内存布局的验证

  • 代码验证内存布局: 验证代码:
   #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>int init=10;int uninit;int main(){printf("code addr:%p\n",&main);printf("init addr:%p\n",&init);printf("uninit addr:%p\n",&uninit);char* heap = (char* )malloc(20);printf("heap addr:%p\n",heap);printf("stack addr:%p\n",&heap);return 0;                                                                                                                             }

运行结果及分析:根据下图运行结果分析,验证了上图的内存分布。
在这里插入图片描述

  • 验证堆向上增长与栈向下增长:

验证代码:

    char* heap1 = (char* )malloc(20);char* heap2 = (char* )malloc(20);char* heap3 = (char* )malloc(20);char* heap4 = (char* )malloc(20);char* heap5 = (char* )malloc(20);printf("heap1 addr:%p\n",heap1);printf("heap2 addr:%p\n",heap2);printf("heap3 addr:%p\n",heap3);printf("heap4 addr:%p\n",heap4);printf("heap5 addr:%p\n",heap5);printf("stack1 addr:%p\n",&heap1);printf("stack2 addr:%p\n",&heap2);printf("stack3 addr:%p\n",&heap3);printf("stack4 addr:%p\n",&heap4);printf("stack5 addr:%p\n",&heap5);                                                                                                    

运行结果:堆向上增长,栈向下减小,与内存分布图一样。
结论:堆栈相对而生。

在这里插入图片描述

  • 验证命令行参数与环境变量:

验证代码:

   int main(int argc,char* argv[],char* env[]){for(int i = 0;argv[i];i++){printf("&argv[%d]:%p \n",i,argv+i);}for(int i = 0;env[i];i++){printf("&env[%d]:%p \n",i,env+i);}return 0;}

运行结果及分析:环境变量与命令行参数这两张表(不是表指向的内容),比栈区大,其中,是先有命令行参数这张表,才有环境变量这张表。
在这里插入图片描述

  • 验证表指向的内容的地址存放:

    注意区分下面代码与上面代码的不同!
    验证代码:

   int main(int argc,char* argv[],char* env[]){for(int i = 0;argv[i];i++){printf("argv[%d]:%p \n",i,argv[i]);}for(int i = 0;env[i];i++){printf("env[%d]:%p \n",i,env[i]);}return 0;}

结果+分析:无论是表还是表指向的项目,都在栈上部的。
在这里插入图片描述

  • 验证静态变量在内存分布中的位置:
    这里就不验证了,直接得出结论:静态变量是存放在初始化数据与未初始化数据之间的。静态变量默认是会被初始化的,哪怕用户定义出来没有赋值,编译器也会初始化。例如int 类型的静态变量,会被编译器初始化为0;

看看一个这样的代码
代码:

   #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>int g_val = 1000;int main(){pid_t id = fork();if(id==0){//子进程while(1){printf("child  pid:%d  ppid:%d  g_val=%d  &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}//父进程                                                                                                                              else{while(1){printf("father  pid:%d  ppid:%d  g_val=%d  &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}return 0;}

运行结果:符合我们预期的,数据本来就是父子进程共享的,除非要写入,进程之间时具有独立性的,写入的时候需要写时拷贝。
在这里插入图片描述

奇怪的现象:

测试代码:

   #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>int g_val = 1000;int main(){pid_t id = fork();if(id==0){//子进程int cnt = 0;while(1){printf("child  pid:%d  ppid:%d  g_val=%d  &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);cnt++;                                                                                                                            if(cnt==3){printf("child change g_val\n");g_val=2000;}}//父进程else{while(1){                                                                                                                                   printf("father  pid:%d  ppid:%d  g_val=%d  &g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}return 0;}

运行结果+分析:奇怪的现象(如下图):同一个变量,子进程尝试对g_val进行写入的时候,会进行写时拷贝,但是为什么地址一样,但是值却不一样呢?
在这里插入图片描述

解释上面的现象:

  1. 地址一样却值不一样,所以这个地址肯定不是物理地址。
  2. 如果是物理地址,绝对不可能在一个地址中存放的内容不一样。

这个地址叫做虚拟地址/线性地址。
结论:我们平时用到的语言的地址全部都不是物理地址,是虚拟地址。所以下面这个图的空间排布的情况不是物理内存,它叫做进程地址空间。
在这里插入图片描述

进程地址空间

每一个进程都有一个task_struct(PCB),PCB里面有该进程的进程地址空间,进程地址空间和内存之间是用一张表(叫做页表:里面存放的是虚拟地址与物理地址)建立关系的,如下图,页表对应一个映射关系,是虚拟地址与物理地址之间的关系。根据虚拟地址可以找到对应的物理地址。下面的结构都是操作系统内部在维护的。

在这里插入图片描述

  • 说明:上面的图就足矣说名问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!

其中,父进程创建子进程后,子进程也会有一个这样的结构,也会有进程地址空间,页表,并且父进程PCB的大部分属性都会被子进程继承下来,页表也会被继承下来(类似浅拷贝),这时父子进程都指向同一个物理内存。以上面的示例分析:当子进程尝试对g_val进行修改时,操作系统会在内存中重新开一个空间,将修改后的值放在这个空间里,再改变页表中g_val的虚拟地址对应的物理地址,注意:改的是物理地址,虚拟地址没有改变,所以上面示例的结果打印出来的地址(虚拟地址)没有改变。如下图:
在这里插入图片描述
根据上面的解释,也能够很好的解释fork()返回值问题了!

什么是进程地址空间?

进程地址空间是数据结构,具体到进程中,是有特定的数据结构对象。
如下图所示:在进程的PCB中,有一个指针,指向自己的进程地址空间,进程地址空间里面,包含一个结构体,结构体里面有很多start和end,划分区域。

在这里插入图片描述

为什么要有地址空间和页表?

  1. 在进程看来,有了页表,可以将物理内存从无徐变为有序,因为页表是有序的。让进程以统一的视角,看待内存;
  2. 将进程管理和内存管解耦合,进程管理与内存管理互不干扰。
  3. 地址空间+页表是保护内存安全的重要手段(拦截非法:例如:野指针,越界问题)。

内存申请问题(malloc/new)

申请内存,本质是进程的地址空间中申请。
这样:可以充分保证:

  1. 内存使用率,不会空转。
  2. 提升new/malloc的速度。

写时拷贝

  1. 为什么需要写时拷贝?
    答:进程之间要做到独立性。
  2. 创建子进程的时候,为什么不直接将父进程的代码和数据拷贝一份给子进程呢?
    答:因为子进程并不是会对父进程的所有数据都要进行写入操作,如果fork()创建子进程的时候,直接拷贝一份代码和数据,会降低fork()的效率。
  3. 为什么是要拷贝呢,只开空间不拷贝行不行?
    答:因为子进程不一定是对这个数据直接进行覆盖式的写入,可以只是对该数据进行局部修改或则是基于之前的值进行操作。

如何做到写时拷贝的?

前面所说的页表,不只是有虚拟地址与物理地址的转换的,还可以带很多选项的,如下图(介绍其中一个:权限):
下图代码字符串"hello Linux"是具有常属性的,不能被修改,当我们尝试去修改的时候,会报错(运行报错)。
是因为在页表有权限,虚拟地址映射到物理地址的时候,会做权限审核,如下图所示,当只有可读权限,没有修改的权限的时候,尝试去修改,就会报错。
在这里插入图片描述

写时拷贝的细节:

当要进行写时拷贝的时候,会将父子进程页表里大部分内容的映射权限设置为只读权限,当父子进程任何一方要去进行尝试写入的时候,操作系统会进行判断,如果是数据段,对数据进行写入时合理的,就会引发缺页中断,操作系统会将权限改为读写,然后写时拷贝后,再把页表对应的条目改为读写。


相关文章:

【Linux学习】进程地址空间与写时拷贝

文章目录 Linux进程内存布局图&#xff1a;内存布局的验证 进程地址空间写时拷贝 Linux进程内存布局图&#xff1a; 地址空间的范围&#xff0c;在32位机器上是2^32比特位,也就是[0,4G]。 内存布局的验证 代码验证内存布局&#xff1a; 验证代码&#xff1a; #include<s…...

Git远程控制

文章目录 1. 创建仓库1.1 Readme1.2 Issue1.3 Pull request 2. 远程仓库克隆3. 推送远程仓库4. 拉取远程仓库5. 配置Git.gitignore配置别名 使用GitHub可以&#xff0c;采用Gitee也行 1. 创建仓库 1.1 Readme Readme文件相当于这个仓库的说明书&#xff0c;gitee会初始化2两份…...

怎样从SQL中分析和提取访问的字段信息?| OceanBase实践

当执行任意一条SELECT SQL语句时&#xff0c;我们如何能够分析出所访问的字段信息&#xff0c;并进一步判断结果集中的每一列数据具体来自于哪些数据库、表以及表中的哪些字段呢&#xff1f;本文将会详细阐述针对此问题的技术解决方案。 应用场景 从 SQL 中解析访问的原始字段…...

MySQL 服务无法启动

常见原因: 检查端口占用&#xff1a; 使用命令行工具&#xff08;如netstat&#xff09;来检查3306端口是否已被其他程序占用,输入netstat -ano&#xff08;Windows&#xff09;或netstat -tulnp | grep 3306&#xff08;Linux/Mac&#xff09;来查找3306端口的占用情况。如果…...

Python贪心算法

贪心算法&#xff08;Greedy Algorithm&#xff09;是一种常见的算法设计策略&#xff0c;它在每一步选择当前最优解&#xff0c;希望通过局部最优解最终得到全局最优解。贪心算法通常适用于满足一些特定条件的问题&#xff0c;例如货币找零、活动选择、任务调度等。贪心算法的…...

牛客网刷题 | BC85 牛牛学数列3

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 牛牛准备继续进阶&…...

quartz定时任务

Quartz 数据结构 quartz采用完全二叉树&#xff1a;除了最后一层每一层节点都是满的&#xff0c;而且最后一层靠左排列。 二叉树节点个数规则&#xff1a;每层从左开始&#xff0c;第一层只有一个&#xff0c;就是2的0次幂&#xff0c;第二层两个就是2的1次幂&#xff0c;第三…...

Python基础学习笔记(五)——选择结构与循环结构

目录 程序的组织结构条件选择结构1. 单分支结构2. 双分支结构3. 多分支结构4. 嵌套&#xff08;分支&#xff09;结构5. 无内容执行6. 条件表达式 循环结构1. 可迭代对象2. range()函数3. for循环语句4. while循环语句5. 结束语句 程序的组织结构 程序的组织结构主要有以下三种…...

Vue插槽solt如何传递具名插槽的数据给子组件?

在Vue中&#xff0c;你可以通过作用域插槽&#xff08;scoped slots&#xff09;来传递数据给子组件。这同样适用于具名插槽。首先&#xff0c;你需要在子组件中定义一个具名插槽&#xff0c;并通过v-slot指令传递数据。例如&#xff1a; 子组件&#xff08;ChildComponent.vu…...

小程序-收货地址管理模块实现

页面结构代码&#xff1a; address-form.vue --->新建地址和修改地址页面 <template><view class"content"><form><!-- 表单内容 --><view class"form-item"><text class"label">收货人</text>…...

【星海随笔】微信小程序(三)

网络数据请求 1.小程序中网络数据请求的限制 出于安全性方面的考虑,小程序官方对 数据接口的请求 做出了如下 两个限制: ① 只能请求 HTTPS 类型的接口 ② 必须将 接口的域名 添加到 信任列表 中 微信小程序只能请求 https 类型的接口 且需要请求的域名必须提前进行设置后,才可…...

pip(包管理器) for Python

pip是什么 pip是Python的包安装程序&#xff0c;即python包管理器。您可以使用 pip 从Python包索引和其他索引安装包。 1. pip 安装 python 包 pip install 包名 例如&#xff1a;pip install pymssql &#xff1a; 使用pip安装数据库驱动包 pymssql 2.pip 卸载 python 包 pi…...

Ubuntu上安装Maven

在Ubuntu上安装Maven的步骤如下&#xff1a; 更新包索引&#xff1a; sudo apt update 安装Maven&#xff1a; sudo apt install maven 验证安装是否成功&#xff1a; mvn -version 以上步骤将会安装Maven并添加到系统路径中&#xff0c;你可以通过运行mvn -version来验…...

java中使用svnkit实现文件的版本管理

java中使用svnkit实现文件的版本管理 一、引入svnKit依赖二、初始化仓库工厂类二、使用svnkit创建本地存储仓库三、svn基本原子操作四、通过原子方法实现简单svn相应操作 一、引入svnKit依赖 <dependency><groupId>org.tmatesoft.svnkit</groupId><artifa…...

了解 Linux 网络卡绑定:提高网络性能与冗余性

在现代 IT 基础设施中&#xff0c;网络性能和可靠性至关重要。对于许多企业和个人用户来说&#xff0c;确保网络的高可用性和冗余性是首要任务之一。Linux 提供了一个强大的解决方案——网络卡绑定&#xff08;Network Interface Card Bonding&#xff0c;简称 NIC Bonding&…...

2024年618购物狂欢节即将来袭!精选五款超值入手数码好物!

618购物狂欢盛宴即将落幕&#xff0c;是时候展现我们的购物智慧了&#xff01;在追求价格优惠的同时&#xff0c;我们更应看重商品的品质与实用性。面对琳琅满目的选择&#xff0c;如何筛选出真正值得拥有的好物呢&#xff1f;为了让大家的购物之旅更加轻松愉快&#xff0c;以下…...

中国AI独角兽资本大冒险

成立不过一年多时间&#xff0c;月之暗面已然成为中国大模型赛道上&#xff0c;最炙手可热的明星公司。 5月21日&#xff0c;华尔街见闻获悉&#xff0c;月之暗面将按照投前估值30亿美元&#xff08;合217.3亿人民币&#xff09;进行融资&#xff0c;完成后依然会是当前中国估…...

项目十二:简单的python基础爬虫训练

许久未见&#xff0c;甚是想念&#xff0c;今日好运&#xff0c;为你带好运。ok&#xff0c;废话不多说&#xff0c;希望这门案例能带你直接快速了解并运用。&#x1f381;&#x1f496; 基础流程 第一步&#xff1a;安装需要用到的requests库&#xff0c;命令如下 pip inst…...

OpenGL学习入门及开发环境搭建

最近学习OpenGL开发&#xff0c;被各种openGL库搞得晕头转向&#xff0c;什么glut, glew glfw glad等等。 可以参考这边博客:OpenGL 下面的 glut freeglut glfw 都是个啥_glx wgl的中文-CSDN博客 glfw是glut的升级版&#xff0c;跨平台的主要处理窗口 事件相关。 glad是glew…...

three.js能实现啥效果?看过来,这里都是它的菜(08)

在Three.js中实现旋转动画的原理是通过修改对象的旋转属性来实现的&#xff0c;通常使用渲染循环&#xff08;render loop&#xff09;来更新对象的旋转状态&#xff0c;从而实现动画效果。 具体的原理包括以下几个步骤&#xff1a; 创建对象&#xff1a;首先创建一个需要旋转…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

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

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...