Linux->进程地址空间

news2024/11/24 2:20:47

目录

前言:  

1. 程序地址空间回顾

2. 进程空间是什么

3. 进程地址空间与内存

4. 进程地址空间和内存的关联

5. 为什么要有进程地址空间


前言:  

        我们在平时学习的过程当中总是听到栈、堆、代码段等等储存空间,但是这些东西到底是什么,我们又怎么理解呢?本篇就为大家解惑。

1. 程序地址空间回顾

         相信大伙们对于这张图片还是很熟悉吧,特别是对于堆和栈这一片区域,堆向上延伸,栈向下扩展,基本上是共同使用这一片空间。这片空间这样使用主要是服务于我们的动态库

        如果大伙想要验证这一张图片的正确性,可以自己验证一下,低地址代码地址长度更小,高地址更大。代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>

int global_uval;									//全局变量未初始化
int global_val = 1;									//全局变量初始化

int main(int argc, char* argv[],char* envp[])
{
	const char* point = "hello world";				//栈内指针和常量区字符串
	char* heap = (char*)malloc(100);				//栈内指针和堆区空间

	printf("代码区起始地址:        %p\n", main);
	printf("常量区字符串:          %p\n", point);
	printf("全局变量初始化:        %p\n", &global_val);
	printf("全局变量未初始化:      %p\n", &global_uval);
	printf("堆区地址:              %p\n", heap);
	printf("栈区地址:              %p\n", &point);
	printf("命令行参数:            %p\n", argv[0]);
	printf("环境变量:              %p\n", envp[0]);

	return 0;
}

         Linux下可以看到地址空间的变化逐渐增加,也正好对应了我们的地址空间的图。

         但是同样的代码在vs下却运行出了不同的结果,如下:

         仔细看vs下运行堆区地址和栈区地址大小与Linux下运行相反,具体原因我也不是很清楚,博主在这里也疑惑了半天,估计是vs悄悄的改了一些实现方法。

2. 进程空间是什么

        看了上面的解释也没有理解,啥是进程地址空间?博主知道你很急,但是你先别急,什么?非要看?好吧,请看下方结论:

进程地址空间不是真实物理地址而是虚拟地址

        知道了吗?没看懂是不?这就对了,还是等我徐徐道来。

        请先看下方代码:

        利用fork创建了一个子进程,count用于计数,当循环走过两次时,a被修改为100

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 
  5 int main()
  6 {
  7   int a = 100;
  8   int count = 0;
  9   pid_t res = fork();    //创建一个子进程                                                                                    
 10 
 11   while(1)
 12   {
 13     if(res == 0)
 14     {
 15       printf("我是一个子进程,a = %d,    &a = %p\n",a,&a);
 16       sleep(1);
 17     }
 18     else{
 19       if(count == 2)
 20         a = 200;
 21       printf("我是一个父进程,a = %d,    &a = %p\n",a,&a);
 22       count++;
 23       sleep(1);
 24 
 25     }
 26   }
 27 
 28 
 29   return 0;
 30 }

         运行结果如下:

         上图中大家有发现什么不对的地方吗?首先,变量a定义在两个进程分流之前,那么父子进程应该拿到的是同一块数据,通过查看地址,再对应值,确实如此,但继续往下看,当计数两次之后,我们将父进程的a修改了,父进程的a也确实被更改了值,但是,父子进程拿到了同一块数据,为什么子进程的a没有被改变呢?更离谱的是它们两个的地址竟然一模一样,这合理吗?这很不合理。

        根据我们学习语言所知,同一块空间只能只能存一个数据,这里竟然存了两个数据,那么根据理论而言,我们能够分析出,我们获取到的地址不可能是真实的物理地址,而是虚拟地址。

         但是根据我们学习的计算机体系结构可以知道,任何一个磁盘上的内容想要跑到CPU中被运行,必须经过被载入内存这一过程,但是上面又显示我们拿到的不是真实的物理地址,就不可能是再内存当中的,难道是我们的冯诺依曼体系在这里出错了?很明显不可能,所以只有一种可能,那就是我们在程序中获取到的地址与我们的内存之间有联系

3. 进程地址空间与内存

        这一部分有些抽象,所以我先为大家讲一个故事吧。(这个故事是博主去别人哪里copy的,博主认为十分生动形象)。

        咱们在学校总是经历过划分三八线这种事情吧。就是给桌子划分区域,假设有小明和小妞两个人,他们有自己的区域,不能越过区域,到别人的领域去。如下:

         我们可以想一下,他们是怎么区分他们自己的区域的呢?不就是记录自己区域的起始结束位置嘛,也就是说小妞想要表示自己的位置,那就是如下:

struct xiaoniu_area{

        size_t begin = 0;

        size_t end = 50;

};

         小明也是同理,也就是说他们自己知道这一片空间的所属就行,不过呢,小明和小妞关系很好,可以亲亲抱抱的那种,所以这一条三八线也就是当作一个提醒罢了。所以呢,小妞有一天要画画,她需要更大的位置操作,那么她就向小明说,我想要多一点点位置,之后还你,小明说:“反正现在我也不用,你用吧”。这样小妞的位置就变为了如下:

struct xiaoniu_area{

        size_t begin = 0;

        size_t end = 70;

};

         那么,相对的小明的位置就被缩减了,图如下:

         看到上面的图,大伙有没有将我们的地址分配图联系起来呢?我们的进程地址也就是通过这个方式表示的,就像是我们的栈、堆等等,它的位置都是能够被调整的

        在Linux当中有一个struct mm_struct这样的结构体,用于表示各个区所对应的位置。如下:

struct mm_struct
{

    //代码区
    unsigned long code_start;
    unsigned long code_end;

   //堆区
    unsigned long heap_start;
    unsigned long heap_end;
    //栈区
    unsigned long stack_start;
    unsigned long stack_end;
}

         也就相当于整个进程地址空间的使用被这样一个结构体划分的明明白白。

        但是这又跟我们的内存有什么联系呢?别着急,接着看。

        我先给出结论:

每个进程都可以独占整个内存空间

         怎么理解上面这句话呢?咋一看是不是感觉很奇怪?仔细一看更奇怪了是不?

        我还是举一个例子。

        有一个10亿家产的富豪,他有2个私生子,这两个私生子不知道彼此的存在,此时呢,富豪对两个孩子分别说了,只要我寄了,这10亿就是你的了,所以呢,这两个还是都认为这10亿自己稳得了。这时,大儿子说,老爸,我想要20万买个车上班,富豪说,行,好好加油,之后小儿子又说,老爸,给我5个亿,我要单开一家公司,做大做强,富豪转身就抽出了七匹狼,你要皮带不要?小儿子就跑了。但是对于大儿子和小儿子来说,他们认为这10还是自己得吗?肯定是啊。

        这里的富豪就是操作系统,10亿就是内存,而两个儿子就是进程,他们可以向操作系统申请空间,操作系统也可以拒绝他们的请求,但是他们能得到的空间是内存大小的空间。这也就表明了,每一个进程都认为自己是可以独占内存的,但是操作系统是能够自己判断是否给你这么多

4. 进程地址空间和内存的关联

进程地址空间通过页表和内存关联起来。

         什么是页表?

        页表就是:进程将自己的代码和数据首先放在虚拟地址空间的对应的区域,在这其中会有一种表结构,叫做页表,页表的核心工作就是完成虚拟地址到物理地址之间的映射,最终我们的可执行程序的代码和数据可以加载到物理内存的任意位置,因为最终只需要建立代码和数据与物理内存之间的映射关系,就可以通过虚拟地址找到物理内存的对应地址。

        咱们可以想到每一个进程都是独立的,父子进程也是不例外的,那么每一个进程也都有属于知道的页表去对应真实的内存。看到这里,想必大家也能回到之前父子进程不同值,地址相同得问题了吧。那就是父子进程得虚拟地址相同,但是这个虚拟地址存在于两张页表当中,这两个页表通过相同的虚拟地址却映射了两个内存空间。

        那么,这两个映射不会映射到一个空间吗?

        不会,还记得我之前讲的mm_struct结构体,内存将这些空间用结构体划分了起来,并且划分之后还会标识这片区域已经被某个进程使用了,那么页表就知道了这篇空间不能使用了,它就会换一片区域去映射。如下:

         看到上面的图,我们可以认为父子进程是完全独立的吗?

        其实不能,因为我们的操作系统是十分会节省空间的,也就是当父进程或子进程的数据没有被改变的时候,两个页表指向的真实空间也是相同的,只有在数据发生改变的时候操作系统会为这一个空间开辟一个新的空间,然后对应给页表,但是页表的虚拟内存没有改变。这一过程被称为写时拷贝

5. 为什么要有进程地址空间

        我们使用malloc时,操作系统在我们申请内存的时候并没有直接的给我们那一片地址,但是这一片空间并不能被其他的进程使用,该片地址会处于一个闲置状态。这篇地址不能使用是这一个进程不能使用,而是其它进程可以使用,但是操作系统会保证数据不会冲突。

        也就是说,我们能够获取到虚拟地址,但是我们没有在意到底有没有实际的物理地址在哪里,反正我们要使用的时候,操作系统能给我们就是了。所以这样做就让我们的进程管理和内存管理之间解耦了。

        还有就是当操作系统要加载一个很大的程序,比如32个G,这样一个程序一定是不可能全部运行起来的,所以操作系统会慢慢的加载,没有被使用的部分就被先睡眠起来了。我们唯一的感受也就是程序变慢了。

        并且,通过进程地址空间,我们操作系统能够清楚地知道你写的进程是否正确,有没有越界?如果越界了,那么操作系统就会让你的进程崩溃,因为你的指针或者其它数据指向了其它进程的区域,保证了进程之间的独立性和安全性。


        以上就是我对进程地址空间的全部理解咯,有问题请帮忙指出啦,博主也是努力进步当中,哈哈。

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

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

相关文章

CSS3新特性

CSS3新特性 1.1、字体图标 何为字体图标&#xff1f; 字体图标展示的是图标&#xff0c;本质是字体。用于处理简单的、颜色单一的图片 字体图标的优点&#xff1a; 灵活性&#xff1a;灵活地修改样式&#xff0c;例如&#xff1a;尺寸、颜色等轻量级&#xff1a;体积小、渲…

Java并发编程(1)—— 操作系统、Linux、Java中进程与线程的区别

一、操作系统中什么是线程和进程 线程和进程都是操作系统中定义的结构&#xff0c;进程是系统中一个独立的活动程序&#xff0c;比如像QQ、网易云音乐&#xff0c;进程是操作系统进行资源分配的基本单位&#xff0c;一个进程中的所有线程共享进程内的资源&#xff0c;而线程则…

【Python学习笔记】第十八节 Python 内置函数

Python 内置函数内置函数就是Python给你提供的, 拿来直接用的函数&#xff0c;比如print&#xff0c;input等Python 内置函数一览表内置函数abs()divmod()input()open()staticmethod()all()enumerate()int()ord()str()any()eval()isinstance()pow()sum()basestring()execfile()…

ARMv8 同步和信号量(读写一致性问题):Load-Exclusive/Store-Exclusive指令详解

目录 一&#xff0c;Local Monitor 与 Global Monitor 1&#xff0c;Local Monitor 2&#xff0c;Global Monitor 二&#xff0c;Exclusive 指令的简单使用 三&#xff0c;Exclusive 示例程序 1&#xff0c;原子自加1程序 2&#xff0c;原子锁程序 四&#xff0c; 多处理…

算法设计与智能计算 || 专题一: 算法基础

专题一: 算法基础 文章目录专题一: 算法基础1. 算法的定义及特点1.1 算法的基本特征1.2 算法的基本要素1.3 算法的评定2 算法常见执行方法2.1 判断语句2.2 循环语句2.3 综合运用3. 计算复杂度4. 代码的重用5. 类函数的定义与使用5.1 定义类5.2 调用类函数1. 算法的定义及特点 …

_hand-2

实现一个迷你版的vue 入口 // js/vue.js class Vue {constructor (options) {// 1. 通过属性保存选项的数据this.$options options || {}this.$data options.data || {}this.$el typeof options.el string ? document.querySelector(options.el) : options.el// 2. 把da…

php mysql高校田径运动会成绩管理系统

第一章 引言 1 1.1 选题背景 1 1.2 编写目的 2 1.3 目标 2 1.4 功能需求 3 第二章 开发工具介绍 4 2.1 PHP 4 2.2 APACHE 5 2.3 MYSQL数据库 5 2.4 运行环境 WINDOWS XP 6 2.5 XAMPP 6 2.6 DREAMWEAVE8 6 2.7 EDITPLUS 7 第三章 需求…

【华为OD机试模拟题】用 C++ 实现 - 热点网络统计(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明热点网络统计【华为OD机试模拟题】题目输入输出描述示例一输入输出示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出…

C++入门知识【超详解】

目录1.认识Chello worldC关键字2.命名空间3.std标准库4.输入输出5.缺省参数6.函数重载7.引用7.1引用的概念7.2引用的场景1.作参数2.作返回值7.3引用的注意点7.4指针和引用的区别8.auto关键字9.基于范围的for循环10.内联函数10.1概念10.2特征11. C98中的指针空值1.认识C hello …

数据结构——单链表(上)

&#x1f307;个人主页&#xff1a;_麦麦_ &#x1f4da;今日名言&#xff1a;“生活总是让我们遍体鳞伤&#xff0c;但到后来&#xff0c;那些受伤的地方一定会变成我们最强壮的地方。” ——海明威《永别了武器》 目录 ​编辑 一、前言 二、正言 3.1链表的概念及结构…

HMM(隐马尔科夫模型)-理论补充2

目录 一.大数定理 二.监督学习方法 1.初始概率 2.转移概率 3.观测概率 三.Baum-Welch算法 1.EM算法整体框架 2. Baum-Welch算法 3.EM过程 4.极大化 5.初始状态概率 6.转移概率和观测概率 四.预测算法 1.预测的近似算法 2.Viterbi算法 1.定义 2. 递推&#xff1…

倒计时2天:中国工程院院士谭建荣等嘉宾确认出席,“警务+”时代来临...

近日伴随公安部、科技部联合印发通知&#xff0c;部署推进科技兴警三年行动计划&#xff08;2023-2025年&#xff09;&#xff0c;现代科技手段与警务工作相结合的方式&#xff0c;正式被定义为未来警务发展的新趋势。 21世纪以来&#xff0c;随着科技的不断发展和创新&#xf…

硬间隔支持向量机算法、软间隔支持向量机算法、非线性支持向量机算法详细介绍及其原理详解

相关文章 K近邻算法和KD树详细介绍及其原理详解朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解决策树算法和CART决策树算法详细介绍及其原理详解线性回归算法和逻辑斯谛回归算法详细介绍及其原理详解硬间隔支持向量机算法、软间隔支持向量机算法、非线性支持向量机算法详细…

JavaScript Date 日期对象实例合集

文章目录JavaScript Date 日期对象实例合集一&#xff0c;使用 Date() 方法获得当日的日期二&#xff0c;使用 getFullYear() 获取年份三&#xff0c;使用getTime() 返回从 1970 年 1 月 1 日至今的毫秒数四&#xff0c;如何使用 setFullYear() 设置具体的日期五&#xff0c;使…

小兔鲜注册页面验证、阶段案例(登录、首页页面)(重点)、小兔鲜放大镜效果——DOM

目录 1. 小兔鲜注册页面验证 2. 阶段案例&#xff08;登录、首页页面&#xff09;&#xff08;重点&#xff09; 3. 小兔鲜放大镜效果 1. 小兔鲜注册页面验证 验证码模块有个小问题&#xff1a; 连续点击获取验证码会导致触发多次计时器&#xff0c;会导致计时出现问题&…

【Stata】从入门到精通.零基础小白必学的教程,一学就fei

视频教程移步&#xff1a;https://www.bilibili.com/video/BV1hK4y1d714/?p4&spm_id_frompageDriver&vd_sourcecc8074e9c81a225f214226065db53d32P3 第二讲 Stata处理数据全流程&#xff08;上&#xff09; P3 - 01:37&#xfeff;内置数据 file example datasets使用…

FastDFS - 分布式文件存储系统

目录一、分布式文件存储1.分布式文件存储的由来2.常见的分布式存储框架二、FastDFS介绍三、FastDFS安装1.拉取镜像文件2.构建Tracker服务3.构建Storage服务4.测试图片上传四、客户端操作1.Fastdfs-java-client1.1 文件上传1.2 文件下载2.SpringBoot整合一、分布式文件存储 1.分…

【MySQL】什么是意向锁 IS IX 及值得学习的思想

文章目录前言行锁和表锁使用意向锁意向锁的算法意向锁的思想JDK 中相似的思想前言 之前看 MySQL 都刻意忽略掉了 IS 和 IX 锁&#xff0c;今天看 《MySQL 是怎样运行的》&#xff0c;把意向锁讲的很通透&#xff0c;本篇博文提炼一下思想。 I: Intention Lock&#xff08;意向…

自建服务器系列-0元搭建linux服务器(windows笔记本)

0元搭建linux服务器一.windows装Centos71.1 centos7 iso镜像1.2 准备U盘1.3 UltraISO 启动盘制作工具安装1.4 准备一台windows 机器1.5 安装过程二 、连接无线wifi三、固定wifi ip3.1 查看网络状态3.2 查看DNS3.3 查看GATEWAY3.4 设置静态IP四、一键快速安装单机版k8s五、申请域…

游戏高度可配置化:通用数据引擎(data-e)及其在模块化游戏开发中的应用构想图解

游戏高度可配置化&#xff1a;通数据引擎在模块化游戏开发中的应用构想图解 ygluu 码客 卢益贵 目录 一、前言 二、模块化与插件 1、常规模块化 2、插件式模块化&#xff08;插件开发&#xff09; 三、通用数据引擎理论与构成 1、名字系统&#xff08;数据类型&#xf…