【Linux】程序地址空间?进程地址空间

news2024/12/23 20:56:27

目录

  • 程序地址空间回顾
  • 进程地址空间
    • 什么是进程地址空间?
    • 进程地址空间与PCB、物理内存、页表和磁盘之间的关系
    • 为什么要存在虚拟地址空间?
    • 重新理解地址空间

程序地址空间回顾

了解进程的运行:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main()
  5 {
  6     pid_t id = fork();
  7     if(id<0)
  8     {
  9         printf("fork error\n");
 10         return 1;
 11     }
 12     else if(id==0)
 13     {
 14         printf("子进程\n");
 15     }
 16     else
 17     {
 18         printf("父进程\n");
 19     }                                                                                                                                                                                                         
 20     return 0;
 21 }

 运行结果:我们会发现这打印的结果乱七八糟,因为它也不知道什么时候该干什么
在这里插入图片描述
 我们让代码睡眠1秒:打印的结果就正常了
在这里插入图片描述
 以前我们学习的内存管理(程序地址空间):
在这里插入图片描述

 为了验证上面虚拟地址,我们运行下面代码:

int global_value = 100;

int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        printf("fork error\n");
        return 1;
    }
    else if(id == 0)
    {
        int cnt = 0;
        while(1)
        {
            printf("我是子进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);
            sleep(1);
            cnt++;
            if(cnt == 10)
            {
                global_value = 300;
                printf("子进程已经更改了全局的变量啦..........\n");
            }
        }
    }
    else
    {
        while(1)
        {
            printf("我是父进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);
            sleep(2);
        }
    }
    sleep(1);
}

在这里插入图片描述
(这种问题出现的原因在下面的为什么要存在虚拟地址空间有讲述)

多进程在读取同一地址的时候,怎么可能出现不同的结果?

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  • 这里地址没变化,说明这里的地址一定不是物理地址,我们以前学习的语音的基本地址(指针)不是对应的物理地址,而是虚拟地址(线性地址)(逻辑地址),这三种说法在Linux之中是相等的,但是在别的地方它们是不同的概念。
  • 在Linux地址下,这种地址叫做 虚拟地址,我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理

 打印出来的地址空间排布,全部都是虚拟地址

感性理解虚拟地址空间:
  我们将进程地址空间类比 “大饼” ,而这个“大饼”就是操作系统给进程画的进程它认为自己是独占系统资源(事实上并不是)。假如我是老板(操作系统),我要给我的员工(进程)画饼,员工(进程)要被管理,每个员工的大饼不尽相同,那我们给员工画的大饼(地址空间)也要被管理,进程要被管理-先描述再组织。所以说,地址空间的本质:是内核的一种数据结构(mm_struct)(PCB也是内核的一种数据结构)


进程地址空间

所以之前说‘程序的地址空间’是不准确的,准确的应该说成 进程地址空间

什么是进程地址空间?

在操作系统中,进程地址空间是进程可以访问的虚拟地址范围。它是操作系统分配给进程的连续虚拟地址块。进程地址空间用于存储进程的代码、数据和堆栈。

每个进程都有自己的专用地址空间,这意味着没有两个进程可以访问同一个虚拟地址。这样做是为了确保进程不会相互干扰。操作系统使用称为内存保护的机制来强制实施此分离。

 进程地址空间分为多个区域,每个区域都有不同的用途。例如,代码区域包含进程的可执行代码,数据区域包含进程的数据,堆栈区域包含进程的调用堆栈。

操作系统通过将虚拟地址映射到物理地址来管理进程地址空间。这是使用称为页表的数据结构完成的。页表是将每个虚拟地址映射到物理地址的表。当进程尝试访问虚拟地址时,操作系统会在页表中查找该地址以查找相应的物理地址。

 进程地址空间是操作系统中的一个关键概念。它允许操作系统将进程彼此隔离并管理系统的内存资源

有关进程地址空间的一些其他注意事项:

  • 进程地址空间的大小通常受系统上可用的物理内存量的限制。
  • 操作系统可以使用各种技术来管理进程地址空间,例如分页和交换。
  • 进程地址空间可以使用共享内存和进程间通信 (IPC) 等机制在进程之间共享。

如何使用进程地址空间的一些示例:

  • 创建进程时,操作系统会为其分配新的进程地址空间。
  • 从文件加载进程时,操作系统会将文件的内容映射到进程的地址空间。
  • 当进程进行系统调用时,操作系统会将进程的虚拟地址转换为物理地址,然后执行系统调用。
  • 当进程终止时,操作系统将释放进程的地址空间。

如何理解区域划分:

在这里插入图片描述

  • 地址空间描述的基本空间大小是字节
  • 32位下,2^32次方个地址 、2^32 * 1字节 = 4GB空间“范围”
  • 每一个字节都要有唯一的地址
  • 2^32个地址是虚拟地址,只要保证唯一性即可

 实例出mm_struct的对象:
在这里插入图片描述
 定义局部变量,malloc new堆空间,是扩大栈区或堆区
 函数调用完毕,free,是缩小栈区或堆区

 这个mm_struct就是操作系统给进程画的“大饼”,告诉进程这4G空间全是给你的。

进程地址空间与PCB、物理内存、页表和磁盘之间的关系

 进程地址空间是进程内存的逻辑视图。它是操作系统分配给进程的连续虚拟地址范围。进程地址空间用于存储进程的代码、数据和堆栈。

 PCB(过程控制块)是一种数据结构,其中包含有关进程的信息,例如其进程ID,父进程ID,状态,优先级和地址空间。(Linux操作系统下的PCB是: task_struct)

 物理内存是计算机的实际内存。它用于存储当前正在运行的所有进程的代码,数据和堆栈。

 页表是将虚拟地址映射到物理地址的数据结构。操作系统使用它来将进程使用的虚拟地址转换为硬件可以访问的物理地址。

 磁盘是辅助存储设备。它用于存储当前未运行的代码,数据和进程堆栈。

进程地址空间与PCB、物理内存、页表、磁盘的关系如下:

  • PCB包含一个指向进程地址空间的指针。
  • 进程地址空间由操作系统使用页表映射到物理内存。
  • 如果没有足够的物理内存可用,操作系统可以将页从进程地址空间交换到磁盘。
  1. 创建进程时,操作系统会为其分配新的进程地址空间。进程地址空间最初为空。然后,操作系统将进程的代码和数据从磁盘加载到进程地址空间中。

  2. 当进程运行时,操作系统使用页表将进程地址空间映射到物理内存。这允许进程访问其代码、数据和堆栈。

  3. 当进程未运行时,操作系统可能会将页从其进程地址空间交换到磁盘。这将为其他进程释放物理内存。

  4. 恢复进程时,操作系统使用页表将页从其进程地址空间加载回物理内存。这允许进程从中断的位置继续运行。

 进程地址空间、PCB、物理内存、页表和磁盘都是现代操作系统的重要组成部分。它们协同工作以允许多个进程在一台计算机上同时运行。

Linux内核代码:
在这里插入图片描述
 这个就是存放起始值:
在这里插入图片描述
磁盘与内存的IO,进程虚拟地址的工作原理:
在这里插入图片描述
操作系统给每个进程画的“大饼”:就是每个地址空间都是2^32。实际上OS每次只会给你申请那么多,它也知道你其实也差不多就用那么多,你要是申请多了,就不让你申请成功,而不是真的给每个进程都4GB空间。
在这里插入图片描述

为什么要存在虚拟地址空间?

  1. 如果让进程直接访问物理内存,万一进程非法越界操作呢?
    在这里插入图片描述
  2. 地址空间的存在,可以更方便的进行进程和进程的数据代码的解耦,保证了进程独立性这样的特征

  我们分析一下上面出现的这种问题:
在这里插入图片描述
在这里插入图片描述
 子进程是由父进程为模板创建的,把父进程的PCB拷贝给子进程,把父进程的地址空间也拷贝给子进程,因为是拷贝过来的,所以在同样的地址有这个global_value。

 因为进程具有独立性,一个进程对被共享的数据做修改的时候,如果影响了其他的进程,那么就不能称为独立了。所以,当子进程要修改global_value的值的时候,在物理内存中会首先将它的值拷贝一份在另外的空间,然后将子进程的页表的映射更改为这个拷贝后的空间的地址,然后再更改它的值,整个过程不会影响到父进程的值,同时也没有影响虚拟地址空间的地址,所以我们父子进程打印出来的&global_value的地址没有变换。

 上面的这种方式叫做写时拷贝,他是操作系统自动做的,对不同进程的数据进行分离。操作系统为了保证进程的独立性,做了很多工作,通过地址空间,通过页表,让不同的进程,映射到不同的物理内存处。

进程 = 内核数据结构 + 进程对应的代码和数据 (两个都是独立的,所以说进程具有独立性)

  1. 让进程以统一的视角,来看待对应的代码和数据等各个区域,方便使用编译器也已统一的视角来进程编译代码(规则是一样的,编完即可直接使用)

重新理解地址空间

 我们写一个调用函数:

int a = 10;
fun()
{
	use a;(仅代表使用a)
}
main()
{
	fun();
}

且看下面调用逻辑:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结:

  1. 我们的可执行程序在没有加载到内存的时候在内部已经有地址了,是在代码的编译期间按照2^32(32位机器)空间进行编址,属于是逻辑地址。所以我们平时说的在32位机器下编译,在64位下编译,差别就是编址空间不同。
  2. 虚拟地址空间,不要只认为操作系统会遵循对应的规则,编译器也要遵守,编译器在编译代码的时候,就是按照虚拟地址空间的方式对我们的代码和数据进行编址的。
  3. 我们将磁盘的代码加载到物理内存,就天然具备了一个物理地址。此刻我们有两套地址,一个是标识物理存在中的代码和数据的地址,一个是程序内部互相跳转的时候的地址-虚拟地址
  4. debug运行起来CPU内部使用的都是虚拟地址

程序运转:

  1. 我们首次运行不是将main函数的物理地址加载进入CPU,而是靠mm_struct里面已经对应好的start将其虚拟地址加载入CPU
  2. main函数的虚拟地址加入后我们通过地址空间,然后页表映射到物理内存上,我们找到main函数后向下执行到调用fun(),然后是将fun();这个指令读取到CPU内部,而不是直接加载物理地址,这个指令内部就有地址(虚拟地址),然后重复上述运行。
  3. 整个过程中CPU都没有见到物理内存

感性的理解上述虚拟地址、页表、物理地址关系:
  我们将虚拟地址认为是你的学号,页表为你的班长,你的宿舍号就是物理地址。你住进学校宿舍就天然就有了一个宿舍号(物理地址),你的辅导员(CPU)使用学号(虚拟地址)需要找到你,就通过你的班长(页表),来找到你的宿舍号最后找到你,你的辅导员(CPU),不关心你的宿舍号(物理地址)是什么,只关心你的本人学号(虚拟地址)。

进程地址空间:
在这里插入图片描述


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

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

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

相关文章

Web安全——HTML基础

HTML 一、对于前端以及后端的认识以及分析二、HTML认知1、网页的组成2、浏览器3、Web标准 三、简单的HTML页面架构四、HTML常见标签1、meta标签2、标题标签3、文本属性4、form表单5、a 标签6、锚文本7、img 标签8、table 表格9、列表标签9.1、无序列表9.2、有序列表 10、框架的…

个人工作总结和计划怎么写

工作总结和计划怎么写1 20__年就快结束&#xff0c;回首年的工作&#xff0c;有硕果累累的喜悦&#xff0c;有与同事协同攻关的艰辛&#xff0c;也有遇到困难和挫折时惆怅&#xff0c;时光过得飞快&#xff0c;不知不觉中&#xff0c;充满希望的_年就伴随着新年伊始即将临近。可…

C++ 重载函数

文章目录 前言一、什么是重载函数&#xff1f;二、重载函数的类型&#xff0c;作用。1. 构造函数重载&#xff1a;2. 运算符重载&#xff1a;3. 函数重载&#xff1a;4. 成员函数重载&#xff1a; 总结 前言 一、什么是重载函数&#xff1f; 在C中&#xff0c;函数重载&#x…

【Linux工具篇】---vim的基本使用

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【Linux专栏】&#x1f388; 本专栏旨在分享学习Linux的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 &#x1f370…

C# 学习(一)概述

今天开始学习 C#&#xff0c;所有学习资料来源于&#xff1a; 菜鸟教程 一、C# 简介 C# 是 .NET 框架的一部分&#xff0c;随之创造出来的语言&#xff0c;所以了解 C# 前&#xff0c;需要知道 .NET 是个什么东西。 1.1 .NET 框架介绍 .NET 是微软提出的 Web 的一种软件开发…

智能文档图像处理技术应用与实践

写在前面智能文档处理面临的技术难题智能文档处理的研究领域● 文档图像分析与预处理● 手写板反光擦除● 版面分析与文档还原 写在最后 写在前面 VALSE 2023 无锡视觉与学习青年学者研讨会近期在无锡国际博览中心举办&#xff0c;由江南大学和无锡新吴区联合承办。本次会议旨…

卡尔曼滤波在目标跟踪中的应用(4)

在前一节内容中&#xff0c;我们学习了二维匀加速运动目标的卡尔曼滤波问题&#xff0c;同时利用MATLAB进行了仿真验证&#xff0c;今天我们继续往下扩展一个维度&#xff0c;学习三维空间下的卡尔曼滤波问题。 话不多说&#xff0c;开整&#xff01;&#xff01;&#xff01; …

【深度学习】6-1 卷积神经网络 - 卷积层

卷积神经网络(Convolutional Neural Network&#xff0c;CNN)。 CNN 被用于图像识别、语音识别等各种场合&#xff0c;在图像识别的比赛中&#xff0c;基于深度学习的方法几乎都以 CNN 为基础。 首先&#xff0c;来看一下 CNN 的网络结构&#xff0c;了解 CNN 的大致框架。CNN…

算法程序设计 之 装载问题(6/8)

一、实验目的&#xff1a; 理解并掌握回溯法与分支限界法的联系与区别&#xff0c;学会构造不同问题的解空间树&#xff0c;用上述两种算法解决装载问题。 实验内容问题描述&#xff1a;有n个集装箱要装上2艘载重量分别为C1和C2的轮船&#xff0c;其中集装箱i的重量为wi&#…

论文浅尝 | DEER:解释实体关系的描述性知识图谱

笔记整理&#xff1a;王润哲&#xff0c;东南大学硕士&#xff0c;研究方向为多元关系抽取 链接&#xff1a;https://aclanthology.org/2022.emnlp-main.448.pdf 动机 实体关系是知识图谱中不可或缺的一层重要信息&#xff0c;它们描述了实体之间的语义关系&#xff0c;这种连接…

【力扣刷题 | 第十二天】

目录 前言&#xff1a; 104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 111. 二叉树的最小深度 - 力扣&#xff08;LeetCode&#xff09; 前序遍历&#xff1a; 后序遍历&#xff1a; 总结&#xff1a; 前言&#xff1a; 今天还是对树的基础题进行刷题&am…

大数据Doris(四十七):开启Steam Load记录

文章目录 开启Steam Load记录 一、停止 Doris 集群 二、在 node3-node5 BE 节点上配置 be.conf 三、重新启动 Doris 集群 开启Steam Load记录 后续执行Stream Load 导入任务后&#xff0c;我们会在Doris集群中会查询对应Stream Load任务的情况&#xff0c;默认BE是不记录S…

【Rust日报】2023-06-20 使用Quickwit、Jaeger和Grafana监控您的Rust应用程序

使用Quickwit、Jaeger和Grafana监控您的Rust应用程序 你可能已经看过了Lucas Palmieri的博客文章Are we observable yet? An introduction to Rust telemetry。如果你还没有看过&#xff0c;我们建议阅读一下&#xff0c;因为它提供了一个全面的介绍&#xff0c;介绍了如何处理…

【Python 基础篇】Python 函数:代码重用的利器

文章目录 导言一、创建函数二、函数参数1、位置参数2、关键字参数3、默认参数 三、函数返回值四、函数的高级用法1、递归函数2、匿名函数3、内置函数 总结 导言 函数是一种在Python中定义和封装可重用代码的重要机制。它们使我们能够将复杂的任务分解为更小的部分&#xff0c;…

【算法与数据结构】15、LeetCode三数之和

文章目录 一、题目二、双指针法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、双指针法 思路分析&#xff1a;我们使用双指针法&#xff0c;但这道题因为要求数组三个元素的和&#xff0c;一共用到了三个指…

新人拿到一个web项目如何使用idea发布运行

本文描述的是一个新手&#xff0c;拿到一个web项目&#xff0c;使用idea如何发布运行。项目中没有非常复杂的元素&#xff0c;只是试着描述应该如何配置相关内容。 内容描述前提&#xff0c;首先请您确认tomcat已经安装&#xff0c;其次确认jdk已经安装&#xff0c;并明确他们在…

基于Java农产品仓库管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

管理类联考——逻辑——技巧篇——论证推理(10-12 道左右)——五大秒杀思路

逻辑考试出题思路分类 论证推理&#xff08;10-12 道左右&#xff09; 论证推理细分思路 假设——补全逻辑假设——引入前提前真后假 - 前假后真建立联系型支持由果推因的削弱由因推果 / 直接引入他因指出不同 / 指出相同五大固定秒杀思路解释类题目评价类题目 论证推理题目…

C语言学习(二十四)---递归与冒泡排序法

在前面几节的内容中&#xff0c;我们学习了指针的相关概念&#xff0c;至此&#xff0c;指针的内容就暂时告一段落了&#xff0c;今天我们将继续向下学习&#xff0c;主要内容为递归和冒泡排序法&#xff0c;好了&#xff0c;话不多说&#xff0c;开整&#xff01;&#xff01;…

【AndroidUI设计】主界面设计-Toolbar的简单使用

文章目录 一、引言二、了解三、编码1、UI设计2、编码 一、引言 描述&#xff1a;需要设计一个主界面&#xff0c;菜单通过主界面的左边界划入&#xff0c;实现点击跳转修改主界面内容的一个效果&#xff0c;并且点击非内容区域恢复原界面的一个效果。做到菜单的弹出&#xff0…