Linux之进程地址空间

news2025/1/11 4:09:14

文章目录

  • 前言
  • 一、是什么
    • 1.例子
    • 2.感性的理解虚拟地址空间
    • 3.现象的具体解释
    • 4.写时拷贝
  • 二、为什么
  • 三、怎么办
  • 总结


前言

内存区域划分:
在这里插入图片描述
在学习C/C++时我们都有接触过内存区域划分这个概念,也知道它表示的是程序加载到内存中不同的数据所分布的不同的区域,但是我们并不清楚它是什么东西,在哪里存储着,为什么要有它,它又是怎样实现的。今天我们就来解决这些疑惑。


一、是什么

进程地址空间是什么?

1.例子

我们先来看这样一个现象:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 int main()
  5 {
  6         pid_t pid = fork();
  7         int i = 10;
  8         if(pid < 0)//函调用失败
  9         {
 10                 perror("fork");
 11                 exit(1);
 12         }
 13         else if(pid == 0)//子进程
 14         {
 15                 int cnt = 0;
 16                 while(1)
 17                 {
 18                         printf("子进程,pid = %d, ppid = %d, i = %d, &i = %p\n",getpid(),getppid(),i,&i);
 19                         if(cnt == 5)
 20                         {
 21                                 i = 5;
 22                         printf("子进程已更改全局变量……\n");
 23                         }
 24                         cnt++;
 25                         sleep(1);
 26                 }
 27         }
 28         else
 29         {
 30                 //父进程
 31                 while(1)
 32                 {
 33                         printf("父进程,pid = %d, ppid = %d, i = %d, &i = %p\n",getpid(),getppid(),i,&i);
 34                         sleep(2);
 35                 }
 36         }
 37         return 0;
 38 }

在这里插入图片描述
我们发现在同一个地址空间的i变量,父进程和子进程访问时获得的值却不是相同的,这是什么原因呢?
首先,我们可以理解,父子进程的值不同是因为进程间具有独立性,但是这里的i的地址居然是相同的!!!我们可以先排除该地址是在物理磁盘上的地址的可能性,因为物理磁盘的同一个地址只能存唯一确定的一个值。
因此,这个地址只能是虚拟地址(线性地址)。在Linux中,特殊情况,我们将这种地址也成为逻辑地址。

2.感性的理解虚拟地址空间

在这里插入图片描述
从前有一个大富翁,他有10亿美元的资产。他有三个私生子,这三个私生子都不知道对方的存在,而大富翁也给他们画饼,说自己就他这一个儿子,自己走后,这10个亿都归他所有,因此他们都认为大富翁的10亿美元是被他们独占。当然大富翁也知道,这三个儿子虽然想着所有的钱都是他的,但是不可能一次性向大富翁要所有的钱,每次最多几十万、几百万的向大富翁要,所以大富翁画的饼从来没有被拆穿过。
我们的操作系统就相当于大富翁,而进程则相当于他的私生子,因为进程之间是相互独立的,因此每个进程都认为自己可以独占操作系统的所有资源,当然进程也不会一次性向申请操作系统申请操作系统的所有资源,一次只会申请一小部分资源。
为了给进程画饼(让进程认为自己独占操作系统资源),操作系统为每个进程都创建了独立的地址空间,地址空间的内容通过页表映射到物理内存中这样每个进程都能独立的运行。
在这里插入图片描述

3.现象的具体解释

父进程和子进程都有自己独立的进程地址空间,也有独立的页表结构。子进程由父进程创建,因此子进程的进程地址空间是拷贝父进程的进程地址空间。刚开始父子进程并未对进程地址空间做修改,因此i值在一开始指向同一个物理内存。
后来,子进程修改了i的值,操作系统通过页表映射发现i的值是两个进程共享的,操作系统为了保持进程的独立性,当子进程或者父进程任何一方尝试对共享的数据做写入,操作系统就会在物理内存上重新开辟一块新的内存空间拷贝原来的数据,然后修改映射关系,使其指向新的物理地址,再进行写入操作。整个修改的过程中,这些工作与父子进程的虚拟地址没有关系,只有底层经过页表映射到了新的物理地址,因此我们观察到的虚拟地址是相同的,但是内容却不同。

4.写时拷贝

父子进程中的任意一方试图对共享数据进行写入,操作系统就会先将原数据进行拷贝,然后改变要写入一方的页表映射,使它映射到新的物理内存中,然后再让进程进行写入的技术称为写时拷贝

二、为什么

为什么存在进程地址空间?

  1. 保证了数据的安全性
    如果进程出现越界非法访问、非法写入,页表会对进程进行拦截。直接对物理内存进行访问,对于账号信息等数据是不安全的(可能出现:意外损坏数据或者恶意读取用户信息等问题)。
  2. 方便进程之间的数据代码的解耦,保证了进程的独立性
    一个进程对数据的修改不会对另一个进程造成影响,保证了进程的独立性。
  3. 让进程以统一的视角看待进程的代码和数据所在的各个区域,同时方便了编译器以统一视角编译代码
    可执行程序再被编译器编译的时候代码和数据再内存中已经有虚拟地址(在磁盘上的这种地址称为逻辑地址),也就是说操作系统和编译器都是遵守地址空间这一理论的。
    在程序被加载到内存成为进程后,每个变量/函数都具备了物理地址。因此,我们现在有两套地址,一套是用于表示物理内存中代码和数据的物理地址;另一套是用于程序内部函数之间进行跳转的虚拟地址。
    加载完毕后,代码的各个区域的地址,操作系统和编译器都已经知道了。进程被调度时,CPU拿到虚拟地址,经过地址空间的页表的映射,就能查到物理地址,通过物理地址访问到代码,然后执行。
    CPU -> 虚拟地址 -> 页表 -> 物理地址 -> 执行。
    也就是说明,CPU运行的整个过程中,CPU都没有见到物理地址,而是用虚拟地址运行程序。

对于磁盘内编译过的可执行程序中的地址不叫虚拟地址,而是叫做逻辑地址。当然对于Linux而言,虚拟地址、线性地址、逻辑地址都是一样的。

三、怎么办

  1. 操作系统要为每一个进程分配地址空间,那么操作系统是否要管理这些地址空间呢?当然是要管理的。
  2. 那么,操作系统怎么管理进程的地址空间
    说到管理,那就是管理数据,管理的方法是先描述,再组织
    首先,进程本身就是需要被管理的,操作系统管理它的方式是将进程的信息存入结构体PCB(即,task_struct)中,再用链式结构将每一个进程的PCB对象组织起来。
    而地址空间也是需要用内核数据结构mm_struct进行管理,OS会为每一个进程创建一个mm_struct(结构体)对象,进行管理。该结构体对象保存在它所对应进程的PCB中。(PCB中的一个属性mm_struct)
  3. 区域划分和调整
    地址空间有很多区域:栈区、堆区、数据段、代码段等,那么进程地址空间是如何进行区域划分的呢?
    举个简单的小栗子:
    在这里插入图片描述
    上小学的小蓝和小粉是同桌,小粉并不想和小蓝一起玩,因此将桌子上用一条“三八线”划分为了两个区域,左边属于小蓝,右边属于小粉两人约定不能过线(即,不能非法访问别人的区域)。
    虚拟地址空间是连续的,因此将地址空间划分为不同区域的方法与上面例子的做法类似,我们用一个区域的起始地址start和终止地址end来调整和维护这一块区域。
struct mm_struct
{
	uint32_t code_start,code_end;
	uint32_t data_start,data_end;
	uint32_t heap_start,heap_end;
	uint32_t stack_start,stack_end;
}

所谓的区域调整,本质就是修改对应区域的start和end的值。
补充说明:
对于区域划分,进程地址空间的划分实际上是这样的:
在这里插入图片描述

0-3G是用户空间,命令行参数和环境变量是在用户空间,这也是为什么我们可以在main函数通过第三个参数env获取环境变量。3-4G是内核空间。


总结

以上就是今天要讲的内容,本文介绍了进程地址空间的相关概念。本文作者目前也是正在学习Linux相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。
最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云服务器数据盘是什么?系统盘和数据盘区别

阿里云服务器系统盘和数据盘有什么区别&#xff1f;系统盘类似Windows电脑的C盘&#xff0c;数据盘相当于其他盘符&#xff0c;数据盘可以有多个而系统盘只能有一个&#xff0c;数据盘可有可无而云服务器系统盘是必须要有的。阿里云服务器网来详细说下阿里云服务器数据盘和系统…

电子企业MES管理系统解决方案

随着信息技术的飞速发展&#xff0c;电子企业面临着日益复杂的数据管理、生产流程和业务决策等问题。如何应对这些问题并提高企业生产效率已成为电子企业的当务之急。本文旨在探讨电子企业MES管理系统的解决方案&#xff0c;以应对电子企业面临的挑战。 在制定电子企业MES管理…

苹果笔不用原装可以吗?推荐性价比最高的平替苹果笔

苹果Pencil的价格令人难以入手&#xff0c;单支就要接近千元&#xff0c;我实在想不通&#xff0c;这款电容笔怎么会以969元的价格出售&#xff1f;尽管苹果的Pencil性能非常不错&#xff0c;但是我认为它的价值还是不值这个价钱。当前国产的平板电容笔正不断完善中&#xff0c…

华为OD机试之羊、狼、农夫过河(Java源码)

羊、狼、农夫过河 题目描述 羊、狼、农夫都在岸边&#xff0c;当羊的数量小于狼的数量时&#xff0c;狼会攻击羊&#xff0c;农夫则会损失羊。农夫有一艘容量固定的船&#xff0c;能够承载固定数量的动物。 要求求出不损失羊情况下将全部羊和狼运到对岸需要的最小次数。只计算…

Zookeeper集群 + Fafka集群

Zookeeper 概述 Zookeeper 定义 Zookeeper是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的Apache项目。 Zookeeper 工作机制 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心…

Python3,使用openpyxl进行excel数据对比,反手一个赞。

openpyxl技能 1、引言2、代码实战2.1 安装2.2 代码实战2.2.1 思路2.2.2 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;我最近在对搞数据对比&#xff0c;你有什么便捷的方法&#xff1f; 小鱼&#xff1a;斗胆问一句&#xff0c;数据量多少&#xff1f; 小屌丝…

lammps教程:Ovito计算RDF方法

本文介绍ovito分析rdf的方法以及参数设置。 专栏已经介绍过多种rdf的计算方法。 如&#xff1a;在in文件中计算rdf&#xff1a; lammps案例&#xff1a;液体平衡态rdf计算模拟练习 编程后处理计算rdf&#xff1a; lammps后处理&#xff1a;python ovito编程计算不同原子对…

Linux运维必备的13款实用工具,你用过吗?

本文介绍几款 Linux 运维比较实用的工具&#xff0c;希望对 Linux 管理员有所帮助。 1、查看进程占用带宽情况-Nethogs Nethogs 是一个终端下的网络流量监控工具可以直观的显示每个进程占用的带宽。 下载&#xff1a;http://sourceforge.net/projects/nethogs/files/nethogs…

Vue+SpringBoot,一款企业级的进销存管理系统,星标2.7k

介绍 Finer 进销存系统是一款面向中小企业的管理软件&#xff0c;基于十多年的中小企业管理经验&#xff0c;由ERP领域的资深专家分析设计&#xff1b;基于J2EE快速开发平台Jeecg-Boot开发&#xff0c;前后端分离架构SpringBoot2.x、Ant Design&Vue、Mybatis-plus、Shiro、…

从零开始搭建属于自己的物联网平台(二)实现基于订阅发布的消息总线

实现基于订阅发布的消息总线 往期链接实现的功能及形式功能设计及代码实现生产者使用redis实现生产者 消费者使用redis实现消费者 配套Subscribe注解实现BeanPostProcessor监听所有的bean创建 EventBus对象 往期链接 从零开始搭建属于自己的物联网平台&#xff08;一&#xff…

测试类型(单元、集成、系统或手动测试)

测试类型(单元、集成、系统或手动测试) 单元测试 单元是系统的单个组件&#xff0c;例如类或单个方法。孤立地测试单元称为单元测试。 优点&#xff1a;速度快/易控/易写 缺点&#xff1a;缺乏现实性/无法捕获所有错误&#xff08;例如与其他组件或服务的交互&#xff09; 单元…

40. 组合总和 II

40. 组合总和 II 回溯 去重

springboot+vue+java旅行旅游景点酒店预订出行订票系统eaog5

线上旅行信息管理系统要求实现以下功能&#xff1a; a.景点管理&#xff0c;展示景点的基础信息&#xff0c;介绍等信息。 b.酒店管理,展示酒店的基础信息&#xff0c;介绍等信息。 c.评价管理&#xff0c;可以查看景点或酒店的相关评价信息&#xff0c;客户消费完&#xff0c;…

Vue3+express实现动态编辑element-plus组件tag标签和select下拉框

需求是利用element-plusd的组件标签tag去实现增加部门的种类&#xff0c;效果图如下&#xff1a; ①在系统设置中添加/删减对应的部门 ②在部门下拉框中弹出自己设置的部门 实现的思路是&#xff1a;通过系统设置中的部门设置增删部门&#xff0c;更新数据库中的部门设置字段…

【Vue2.0源码学习】虚拟DOM篇-Vue中的DOM-更新子节点

文章目录 1. 前言2. 更新子节点3. 创建子节点4. 删除子节点5. 更新子节点6. 移动子节点7. 回到源码8. 总结 1. 前言 在上一篇文章中&#xff0c;我们了解了Vue中的patch过程&#xff0c;即DOM-Diff算法。并且知道了在patch过程中基本会干三件事&#xff0c;分别是&#xff1a;…

小型流水线模型的制作

1. 功能说明 本文示例将实现R327a样机——一款5工序的小型流水线模型&#xff0c;包含铸锭送料、传送、搬运、模拟加工、码垛5个工序。 2. 结构说明 小型流水线主要是由铸锭送料结构、传送机构、搬运机构、模拟加工机构、码垛机构5部分组成。 3. 电子硬件 在这个示例中&#xf…

Qt文件系统源码分析—第七篇QFileSelector

深度 本文主要分析Windows平台&#xff0c;Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次&#xff0c;再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice Q…

【SpringMVC】| 一文带你搞定SpringMVC的@RequestMapping注解

目录 环境搭建 Request注解的功能 1. RequestMapping注解的位置 2、RequestMapping注解的【value】属性 3、RequestMapping注解的【method】属性 4、RequestMapping注解的【params】属性&#xff08;了解&#xff09; 5、RequestMapping注解的【headers】属性&#xff0…

Attention 和 Transformer

本文参考d2l&#xff0c;搭配知识点和代码&#xff0c;助力一口气搞懂Transformer&#xff0c;参考&#xff1a; chapter_attention-mechanisms-and-transformers https://d2l.ai/chapter_attention-mechanisms-and-transformers/index.html 目录如下&#xff1a; x.1 Queri…

项目复盘四步:怎么做才有成效?这些关键点不可忽略

在项目管理中&#xff0c;及时复盘是非常重要的&#xff0c;因为只有通过反思和分析&#xff0c;才能找到差距存在的原因。 复盘分析的第一步是回顾目标 因为目标是工作开展的关键。在执行项目的过程中&#xff0c;要始终朝着所设定的目标去努力实现。计划和现实会存在偏差&…