Linux程序的地址空间

news2024/12/23 13:34:53

Linux程序的地址空间

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容深刻理解了什么程序或者进程的地址空间,以及它存在的意义,和操作系统内部是如何实现进程地址空间的

文章目录

  • Linux程序的地址空间
    • 1.一个测试代码
      • 运行的结果:
      • 1.1可以得到的结论
    • 2.引入地址空间
      • 2.1小故事
      • 2.2 代码区,数据区,堆区,等等这些区域该如何理解
    • 3. 地址空间是什么?为什么?怎么办?
      • 3.1为什么要有地址空间
      • 3.2 malloc的本质
      • 3.3 重新理解地址空间

1.一个测试代码

我们先运行一个测试代码:

#include<stdio.h>
#include<unistd.h>
#include<assert.h>



int g_val = 100; //全局变量

int main()
{

pid_t id = fork();

assert(id >= 0);

if(id == 0)
{
 //child
 while(1)
 {
   //此处反斜杠是为了让代码可以换行
   printf("我是子进程,我的PID是:%d,我的PPID是:%d,g_val:%d, &g_val:%p\n",\
       getpid(), getppid(), g_val, &g_val);                                                                      
   sleep(1);
   g_val++;
 }
}
else
{
 while(1)
 {
   //父进程
   printf("我是父进程,我的PID是:%d,我的PPID是:%d,g_val:%d, &g_val:%p\n",\
       getpid(), getppid(), g_val, &g_val);
   sleep(1);
 }
}
return 0;
}

运行的结果:

image-20231106170746600

1.1可以得到的结论

我们发现竟然指向相同地址的变量,却出现了不同值的情况,这其实是因为发生了写时拷贝,并且我们佐证了,子进程可以对全局变量进行修改并且不影响父进程全局变量的值,从而证明了进程具有独立性。

同时我们也可以看出这里的地址不是物理地址,因为假设是物理地址,我们不可能在同一块物理地址上,取到不同的值,所以我们认为这是一个虚拟地址or线性地址

2.引入地址空间

2.1小故事

我们用一个大富翁和私生子的故事来引入地址空间的概念:

大富翁有十亿美金,但是他有四个私生子(A , B, C , D);

image-20231106223351434

A:做生意的 | B:卖化妆品 | C:哈弗读书 | D:高中辍学的混混

并且ABCD并不知道彼此的存在,并且他们都坚信自己最终能够继承大富翁的遗产;

某一天大富翁对着A说:你好好做你的生意,将来等我去世的时候我的十亿家产全都是你的。

对B说:你的化妆品卖的挺好,你听老爹的话,等老爹到时候西去的时候,我的家产都是你的。

同样对C和D都是一样的画饼。

所以现在ABCD每个人脑子里都有一张饼,就是老爹的十亿。😆

有一天A来找到大富翁,告诉他说自己需要五万美金,做生意需要买一块手表,会见自己和合作伙伴,但是自己的生意目前周转不开,大富翁一想,才五万美金,大手一挥直接给A了。

C联系大富翁,并给他说:自己在学校念书,没有生活费了,想问老爹要1000美金的生活费。老爹一听,小钱大手一挥又给了。(以上两个是进程向操作系统申请较小的内存空间,操作系统当然允许)

有一天D找到大富翁说:我在社会上遇到一点事,需要一些钱摆平一下,你给我5000万美金吧。老爹一听,说:一边去,我这么多钱也不能这么花。D骂骂咧咧的走了。(进程向操作系统申请很大的内存空间,操作系统拒绝了)

…这是ABCD也都会认为自己最终如果想要那十亿美金**(饼)**,他们都还是会获得的。

但其实如果ABCD每个人向大富翁索要几千几万,大富翁都会给予。但是如果向大富翁索要很多,几千万几亿的情况下,大富翁是完全可以直接拒绝的。

这里我们引入第一个概念:

进程的地址空间:就是大富翁给每个私生子画的饼。

那么最后揭晓:十亿美金相当于内存,大富翁相当于操作系统


那么我们想想大富翁有没有必要将自己的“饼”管理起来呢?

答案是有必要的先描述在组织,因为如果大富翁的儿子多了,并且他画的饼多了之后,他也会忘记的。

2.2 代码区,数据区,堆区,等等这些区域该如何理解

我们用一个小故事引入:

时间来到小学时代,有一对同桌,小花和小胖,两个人**共用一张大桌子。(内存空间)**然而小胖,不爱干净,男孩子很爱运动,所以身上老有一股汗味。小花就不满意了,二话不说先把小胖揍了一顿,告诉他:回家之后好好洗一洗,回去告诉你爸爸妈妈,我们上幼儿园要讲卫生。第二点,你从今天开始不能再欺负我了,要不然我再揍你一顿。

小花告诉小胖:我们以中间这根线为界谁都不许越界,不能把你的东西放到我这边来,也不能把你的胳膊放到我这边来。

小花画这条线的本质:叫做区域划分!如下这样:

struct area
{
    int start;
    int end;
}
struct area xiaohua_area = {1, 50};
struct area xiaopang_area = {50, 100};

地址空间本质就是线性结构的

image-20231107145145203

不管是32位还是64位机器,我们并不需要记录地址空间是多少GB的,原因是:不管是多少位的机器,在操作系统刚刚开始加载的时候就确定好了。

那么有了之前的铺垫,我们就可以地址空间是怎么在mm_struct中划分的;

struct mm_struct ---- 4GB
{
    long code_start; //代码区的起始
    long code_end; //代码区的结束
    long init_start;
    long init_end;
    ....
    long brk_start; //堆区的起始
    long brk_end; //堆区的结束
    long stack_start; //栈区的起始
    long stack_end; //栈区的结束
}

那么如果限定了区域,那么区域之间的数据是什么?

例如[1000,2000],1000和2000被称作我们的地址,那么他们之间的区域就被称作虚拟地址or线性地址

那么我们故事在继续…

小花和小胖画好了三八线之后,一段时间,两个人都很受规矩,但是小胖却很调皮,小胖总是将自己的胳膊放到三八线左侧,或者将自己的书包和都扔到小花的桌子上,这强烈的引起了小花的不满,小花就将小胖揍了一顿,然后更改了三八线的位置…

那么对于小花来说,叫做区域扩大。对于小胖来说叫做区域缩小;

体现在mm_struct中呢就是:

xiaohua_area.end = 75;
xiaopang_area.start = 75;

那么对应到我们的地址空间上,对于我们的栈区,堆区等的区域扩大和缩小也是一样的,都是更改我们的startend.

3. 地址空间是什么?为什么?怎么办?

数据和代码真正是存储在内存中。

是什么?

虚拟地址空间是一种在操作系统内部为进程创建出来的具体的数据结构对象,让进程以同意的视角来查看内存。

image-20231107153416685

虚拟地址会经过也变转化为物理地址,我们平时使用的地址都是虚拟地址,当我们有了虚拟地址,我们就可以通过页表找到物理地址中对应的位置,从而将物理地址中存储的内容放到我们的CPU中。

接下来我们用一幅图来解释最开始的父进程和子进程,查看相同地址的内存,却看到不同值的问题。

首先操作系统为每一个进程维护一张页表,用来存储虚拟地址和物理地址的映射关系。

image-20231107155544493

最开始的时候父子进程的虚拟地址都是指向最开始的g_val的位置,但是当子进程尝试修改g_val的时候,这时候操作系统会在物理地址的另一个位置拷贝一份g_val的值,然后修改,并且将这个物理地址存储到子进程页表刚刚指向原物理地址的位置。

并且这里也是体现了进程的独立性。

同时我们这里也可以解释最开始的之前的代码fork()函数貌似有两个返回值的问题:

首先fork在返回的时候,父子进程都有了,return两次,id是不是也是pid_t类型定义的变量呢?那么返回的本质就是写入,父子进程谁先写入,就发生了写时拷贝。造成了一个变量,有两个值的情况。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{     
	pid_t ret = fork();
	if(ret == 0)
	{ 
		//子进程
		printf("我是子进程,我的pid是:%d,我的父进程是:%d",getpid(),getppid()); 
		sleep(1);        
	}
	else if(ret > 0)
	{    
		//父进程
		while(1)
		{ 
			printf("我是父进程,我的pid是:%d,我的父进程是:%d"getpid(),getppid());
			sleep(2);   
		}                          
	}                                                                                            
	return 0;                                                                                  
} 

3.1为什么要有地址空间

如果没有地址空间,我们的进程的pcb只能直接指向物理内存中的该进程相关的数据和代码。

这样就会产生很多不安全的问题,以及效率问题。

①防止地址随意访问,保护物理内存和其他进程

②将进程管理和内存管理解耦合

③可以让进程以统一的视角,看待自己的代码和数据

3.2 malloc的本质

当我们向操作系统申请内存,操作系统立马给你,还是需要的时候再给你呢?

那么毫无疑问肯定是我们需要的时候才会给我们。

①操作一同不会允许任何的浪费和不高效

②申请内存 != 立马使用

③在你申请成功之后,和你使用之前,有很小一段的时间窗口,----这个空间没有被正常使用,但是别人也用不了,—闲置状态。

那么malloc的时候申请空间,我们操作系统只会再地址空间上对应的堆区进行申请空间,那么这里就是我们之前提到的,操作系统会为我们的堆区进行区域扩大,也就是去操作我们的mm_struct中的brk_endbrk_start,然后去页表中存储,但是不会再物理地址上去帮我们申请。相当于做了一半的映射,这时候直到当我们的进程需要用到这片地址的时候,操作系统才会帮我们去完成页表和物理地址的映射。

当操作系统检测到我们需要内存的时候才给我们申请,或者检测到我们的代码和数据不在内存,当我们需要的时候给我们换入,这样的操作叫做缺页中断

总结一句就是malloc在申请空间的时候,我们的操作系统不会立马分配空间,而是在我们需要使用的时候才给我们分配,这就是malloc的本质。

并且正是因为有了地址空间的存在,我们的进程也不关心,它的数据和代码存储在物理地址的任意位置。

**进程管理:**操作系统管理进程和地址空间的过程,叫做进程管理。

内存管理: 操作系统管理页表和物理内存之间的映射的过程,叫做内存管理。

并且操作系统将进程和物理内存之间分成进程管理内存管理的过程,叫做解耦合。

3.3 重新理解地址空间

首先我们想一想我们的程序在被编译的时候,没有被加载到内存,请问,我们的程序内部有没有地址呢?

答案肯定是有的,源代码被编译的时候,就是按照虚拟地址空间的方式进行对代码和数据早就已经编好了对应的编址。

我们不能认为虚拟地址这样的策略只会影响操作系统,还要让我们的编译器也要遵守这样的规则。(Linux系统中的可执行文件的格式是ELF格式)

进程的代码和数据需要一直在内存中吗?

答案是不一定的,就算是我们要运行一个进程,我们操作系统也不会一次将这个进程所对应的代码和数据加载到内存中。

操作系统一般是边加载边执行,这也是地址空间的意义。

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

2000-2021年全国各省市城乡平均受教育年限数据(分城镇和农村)

2000-2021年全国各省市城乡平均受教育年限数据&#xff08;分城镇和农村&#xff09; 1、时间&#xff1a;2000-2021年 2、范围&#xff1a;全国及31省 3、来源&#xff1a;人口与就业统计年鉴 4、指标包括&#xff1a;城乡平均受教育年限 、6岁以上总人口 未上过学、…

【手把手教你】训练YOLOv8分割模型

1.下载文件 在github上下载YOLOV8模型的文件&#xff0c;搜索yolov8&#xff0c;star最多这个就是 2. 准备环境 环境要求python>3.8&#xff0c;PyTorch>1.8&#xff0c;自行安装ptyorch环境即可 2. 制作数据集 制作数据集&#xff0c;需要使用labelme这个包&#…

跨境电商,用指纹浏览器还是VPS?有何区别?

目前做跨境电商的小伙伴基本都是选择vps或者指纹浏览器来防关联。不过随着指纹浏览器的普及&#xff0c;越来越多人选择使用指纹浏览器&#xff0c;还没了解过指纹浏览器的小伙伴可能还在犹豫&#xff0c;vps和指纹浏览器到底哪个更好呢&#xff1f; Vps就是一个虚拟服务器&…

超级英雄云计算的技术之旅

超级英雄云计算的技术之旅 超级英雄云计算的技术之旅摘要引言可变参数&#xff1a;Java的超级工具可变参数的用途1. 编写通用工具方法2. 构建日志记录工具3. 构建数据验证工具 云计算在智能家居中的应用1. 远程控制智能设备2. 数据分析和智能决策3. 安全和隐私4. 智能家居应用开…

视频监控管理平台EasyCVR定制算法如何操作?包含哪些?

视频监控平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c;也能支持视…

猫主食罐头哪家好?最受欢迎的猫主食罐头Top5推荐!

亲爱的猫咪主人们&#xff0c;你是否曾为挑选适合猫咪的猫罐头品牌而感到困惑&#xff1f;我理解你们的困扰&#xff01;当初作为养猫新手的我&#xff0c;也深感这个问题不易解决。 猫咪是如此治愈我们的宠物&#xff0c;让我们沉浸在幸福中&#xff0c;因此我经常流会去各种…

未来已来,“码”上见证---通义灵码

为了撰写一份关于通义灵码的产品测评&#xff0c;我将构建一个基于提供的产品介绍和评测内容要求的框架给大家介绍这款产品。 功能使用维度 代码智能生成 使用场景&#xff1a;开发中遇到需要编写新功能、单元测试、或对现有代码进行注释时。 使用效果&#xff1a;预期通义灵…

Web服务器的搭建

网站需求&#xff1a; 1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个网站目录分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于www.openlab.com/student 网站访问学生信息&#xff0c;www.openlab.com/data网站访问教…

Harmony OS—UIAbility的使用

概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互。UIAbility也是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面。一个应用可以有一个UIAbility&#xff0c;也可以有多个UIAbility&#xff0c;类似于Android 的 Activity&#xff0c…

14.序列化和文件的输入/输出 保存对象

14.1 保存对象状态 你已经奏出完美的乐章&#xff0c;现在会想把它储存起来。你可以抓个文房四宝把它记下来&#xff0c;但也可以按下储存按钮(或按下File菜单上的Save)。然后你帮文件命名&#xff0c;并希望这个文件不会让屏幕变成蓝色的画面。 储存状态的选择有很多种&…

批量迁移redis实例的key

我们知道migrate 命令可以迁移redis的多个key&#xff0c;但是如果redis的key有非常多&#xff0c;那用起来就很不方便了。 所以下面分享一个脚本来实现批量key的迁移&#xff0c;主要使用的命令为dump和restore 脚本如下&#xff1a; #!/bin/bash redis-cli -h host1 -p 63…

第19章_体系结构

文章目录 1. 逻辑架构剖析1.1 服务器处理客户端请求1.2 Connectors1.3 第1层&#xff1a;连接层1.4 第2层&#xff1a;服务层1.4.1 SQL Interface: SQL接口1.4.2 Parser: 解析器1.4.3 Optimizer: 查询优化器1.4.4 Caches & Buffers&#xff1a; 查询缓存组件 1.5 第3层&…

详细创建Prism架构wpf项目

方案一&#xff1a; 1.创建一个普通wpf项目 2、安装NuGet包&#xff1a;Prism.DryIoc 3、App.xaml.cs中: 将原本的父类Application改为&#xff1a;PrismApplication&#xff0c;并且实现抽象类 CreateShell方法中写上&#xff1a;”return Container.Resolve<MainWindow>…

YOLO目标检测——交通标志识别数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;交通标志识别数据集在自动驾驶、交通安全监控、智能交通系统、驾驶员辅助系统和城市规划等领域都有广泛应用的潜力数据集说明&#xff1a;交通标志识别数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含有交通标识6类…

电源模块的测试方法都有哪些?如何用ATECLOUD进行测试?

电源模块的测试方法主要包括以下步骤&#xff1a; 连接设备&#xff1a;通过LAN通讯总线、测试夹具以及其它线缆对测试所需的设备和需要测试的电源模块进行连接&#xff0c;同时接入纳米box进入登录ATECLOUD云测试平台。 创建测试项目&#xff1a;根据所以的设备仪器搭建所需…

clumsy 0.3 发布,十年前推出的差网络环境模拟工具

clumsy 0.3 现已发布&#xff0c;距离 v0.1 版本已经过去了十年的时间。clumsy 能在 Windows 平台下人工造成不稳定的网络状况&#xff0c;方便你调试应用程序在极端网络状况下的表现。 0.3 二进制文件与一年半前发布的 0.3 RC4 相同。 将滞后时间上限提高到 15 秒改用 zig 0.…

彻底改变日常生活:面向消费类应用的物联网

在本文中&#xff0c;我们将探讨消费应用的物联网世界&#xff0c;包括其关键用途、优势、挑战以及它为消费者带来的令人兴奋的未来。 物联网 (IoT) 正在重塑我们的生活、工作以及与环境互动的方式。 通过物联网&#xff0c;日常物品和设备连接到互联网&#xff0c;创建一个可以…

jenkins部署job

apt install fontconfig openjdk-11-jre wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/2.429/jenkins.wardeb包安装 wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/debian-stable/jenkins_2.414.3_all.debdpkg -i jenkins_2.414.3_all.deb 访问 http://…

波形的哪些事

一.静音波形制造(波形卡顿制造) 二.pop波形制造 三.示波器探头设置 四.示波器的差分输入和单端输入的接法不一样 差分的接法&#xff0c;需要配差分探头(如下图)&#xff0c;差分探头的两个脚分别和功放输出通道的两个脚连接 单端的接法&#xff0c;需要单端的探头&#xff0c…

【算法与数据结构】216、LeetCode组合总和 III

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题可以直接利用77题的代码【算法与数据结构】77、LeetCode组合&#xff0c;稍作修改即可使用。   …