4进程地址空间

news2025/7/9 17:59:31

文章目录

  • 前言
  • 1. 概念引入
  • 2. CPU和物理内存关系
  • 3.何为进程地址空间
  • 4. 为什么存在地址空间?

前言

本节主要是讲解进程地址空间,区分和物理内存地址空间的差别,并且向读者解释四个疑问:

怎样验证地址空间的排布; 进程地址空间是什么; 进程地址空间和物理内存之间的关系; 为什么要存在地址空间;

而本篇讲解时候,是以32位计算机为例.


1. 概念引入

我们在学习C/C++时候,应该学习过内存布局,以及了解各种变量的存储位置,例如局部变量存储在栈区,动态申请的内容在堆区,全局变量,常量等在数据常量区,如果用一张图来表示,如下:

在这里插入图片描述

(已初始化和未初始化区指全局变量) (图1)

上图是否正确呢?我们可以用以下程序进行验证数据分布:

#include <stdio.h>
#include <stdlib.h>
int g_unval;
int g_val = 100;
int main(int argc,char* argv[],char* env[])
{
    printf("代码区地址          :  %p\n",main);
    const char* p = "hello world!";
    printf("字符常量区地址      :  %p\n",p);
    printf("已初始化全局区地址  :  %p\n",&g_val);
    printf("未初始化全局区地址  :  %p\n",&g_unval);
    char* q0 = (char*)malloc(10);
    char* q1 = (char*)malloc(10);
    char* q2 = (char*)malloc(10);
    char* q3 = (char*)malloc(10);
    char* q4 = (char*)malloc(10);
    printf("堆区地址            :  %p\n",q0);
    printf("堆区地址            :  %p\n",q1);
    printf("堆区地址            :  %p\n",q2);
    printf("堆区地址            :  %p\n",q3);
    printf("堆区地址            :  %p\n",q4);
    printf("栈区地址            :  %p\n",&q0);
    printf("栈区地址            :  %p\n",&q1);
    printf("栈区地址            :  %p\n",&q2);
    printf("栈区地址            :  %p\n",&q3);
    printf("栈区地址            :  %p\n",&q4);
    printf("第一个命令行地址    :  %p\n",argv[0]);
    printf("最后一个命令行地址  :  %p\n",argv[argc-1]);
    printf("环境变量地址        :  %p\n",env[0]);
    return 0;
}

根据运行结果,能够看出从代码区地址开始,一直到环境变量地址区域,按照地址类型都是逐渐增大的, 并且能发现堆区地址向上生长,栈区地址向下生长,堆区地址向上生长,所以上图是正确的

既然我们知道图1是正确的,那么请问,图一这个空间是我们的所说的物理内存空间吗?

答案是否定的,它只是一个虚拟空间,是由人为逻辑而想象出来的,可以通过下面程序进行验证.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int g_val = 0;
int main()
{
    printf("begin <--------> g_val = %d\n",g_val);
    pid_t id = fork();
    if(id == 0)
    {
        g_val = 10;
        for(int i = 0;i<5;i++)
        {
            printf("child --->pid:%d--->ppid:%d--->[g_val:%d]-->[&g_val:%p]\n",getpid(),getppid(),g_val,&g_val);
            sleep(1);
        }
    }
    else if(id > 0)
    {
        g_val = 100;
        for(int i = 0;i<5;i++)
        {    
             printf("parent--->pid:%d--->ppid: %d--->[g_val:%d]-->[&g_val:%p]\n",getpid(),getppid(),g_val,&g_val);
        	sleep(1);
        }
    }
    return 0;
}

在前面进程章节我们提过,fork以后父子进程共享代码,数据各自一份,而下面的运行结果显示,数据确实私有,但是g_val的地址父子进程竟然一样,也就是说同一块内存空间,竟然存了两份数据,这明显是不可能的.

所以图一根本不是我们所说的物理内存空间,那它是什么呢? — 进程地址空间,所以这也同时解释了,为何全局变量的生命周期会跟着程序一起结束. 因此这里再澄清一个概念,在我们以前学习的任何语言中,所提到的内存概念其实指的是进程地址空间,说内存是为了让我们进行程序.


2. CPU和物理内存关系

内存被人为的划分了很多区域进行编号,称为地址,为了方便进行查找,就像我们的门牌号一样.

CPU是通过什么将地址、数据和控制信息传到内存中的呢?电子计算机能处理、传输的信息都是电信号,电信号当然要用导线传送。在计算机中专门有连接CPU和其他芯片的导线,通常称为总线。总线从物理上来讲,就是一根根导线的集合,根据传送信息的不同,总线从逻辑上又分为3类,地址总线、控制总线和数据总线。

比如CPU想要获取地址编号为3的内存的数据,他们的过程如下:

(1)CPU 通过地址线将地址信息3发出。
(2)CPU 通过控制线发出内存读命令,选中存储器芯片,并通知它,将要从中读取数据。
(3) 存储器将3号单元中的数据8通过数据线送入CPU。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Zz1NSXp-1668515249628)(https://gitee.com/du-weiwei/typora/raw/master/image-20220310185435231.png)]

然而一根线只能发送一个高低信号(表示0或1),因此地址线是有很多根的,我们典型的说计算机是多少位系统,就是说的地址线有多少根.

假设地址线有32根,那么就是说CPU可以访问2^32个空间单位(一单位是1字节),即可以访问2^32个字节 = 4GB.

现在我们再回头过来看一下图一,最上面博主所空出来的那1G,这里面主要是用来驻OS的.


3.何为进程地址空间

我们知道进程是由PCB控制的,而进程地址空间其实是PCB中的一个结构体(mm_struct),这个结构体内部定义了存储区地址的起始区域.

例如栈区

unsigned int stack_start;
unsigned int stack_end;

当进程被创建以后,stack_start等就会被存储起始地址值,用于表示栈区地址的起始范围,以及表示在该区域只能存储局部变量等,其他区域同理.由于是32位计算机,这个结构体所记录的总的起始区域大小就是end-start=2^32.而这个mm_struct就是进程地址空间,也就是图一,但是图一并不存在现实中,是我们所抽象出来的一个概念,而该结构体所记录的地址还需要一个被称为页表的结构通过某种转换映射到真实的物理内存地址中.他们之间的关系如图

做个类比可能更好理解这个虚拟的进程地址空间,一位资产只有10亿的富翁分别对10个互不相识的人说,我这里有100亿,你们帮我做事情,这钱就是你的了,如果在做事情的过程中,你需要一部分钱就给我说,我给你,等事后完成,需要扣除这一部分. 这时候是不是每个人都认为自己有了100亿美金,即使并没有真的得到?

而这个富翁就是物理内存,10亿就是内存的真实大小,这多个人就是计算机中的进程.而这个100亿就是存在于想象中,是一种抽象,就像进程地址空间,因为进程地址空间本质上只是一个记录区域的结构体.然后其中一个人说,我需要买工具,你给我点钱,于是富翁就给他一点钱,这个过程就像代码中的部分变量,需要存储数据,然后便在这个虚拟的想象内存中要一部分空间,然后页表把这个空间映射到真实的内存中进行存储.

因此,进程地址空间和物理内存之间的联系便是这个页表.

  • 区域划分本质是将线性地址空间划分成一个一个的[start,end]区域

  • 虚拟地址本质便是[start,end]之间的各个地址


4. 为什么存在地址空间?

可能读者会好奇,为什么要多一个进程空间地址呢?直接存放数据不香吗?

通过前面我们可以知道内存是被划分为很多个小单元(字节)的,如果我们的进程在内存里面,然后各种数据也在内存里面,在进程空间地址中,能看到其地址是非常有序的,而通过页表映射到内存中的实际位置时,是乱序的,也就是说物理内存的存储原则是哪里有空我就存哪里.

如果我们直接存在内存里面,这就会导致一个非常糟糕的问题,寻址麻烦,其次对于指针访问越界应该怎样处理?

因此存在地址空间的第一个原因是

保护物理内存不受到任何进程内的地址的直接访问,出现越界访问,即保护系统进行合法性检测

观察上图,可能会发现页表有两部分,其左边存放的是虚拟地址,右边则是标明其所指向内存空间是否具有读写权限

而操作系统的任务之一是进行内存管理,如果没有进程空间地址,我们想创建一个进程,第一步是获取数据,第二步是获取内存空闲的位置,第三部是给这些数据进行存储,并且还要想办法记住这些地址; 也就是说进程的内存管理将和进程密不可分(强耦合).如果有进程空间地址的话,操作系统只需要给进程一个空间地址和页表,进程就可以通过页表自动向内存申请空间,而这些数据存储的地址用户角度看来还是顺序存储的.当进程结束时,也只需要通过页表进行高速内存自行释放.

因此存在地址空的第二个原因是

将操作系统的内存管理和进程管理进行解耦,管理比较轻松

在计算机中,还有一个外存磁盘,而磁盘中的数据存储形式就是按照代码区,数据区,栈区,堆区有序的形式存着数据,进程空间地址的区域划分本质和磁盘一样.也就是说

让每个进程都可以和磁盘一样,以同样的方式看待代码和数据,方便查找代码和数据

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

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

相关文章

[附源码]java毕业设计基于web的球类体育馆预定系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

数据链路层(2层 Data Link Layer)

1、数据链路层属于2层 2、传输单元&#xff1a;帧 帧格式&#xff1a; 802.3 有线网卡 802.11 无线网卡&#xff0c;无线路由器都支持802.11 802开头的都是国际标准&#xff0c;是由IEEE国际学术组织制定的标准 3、帧结构的构成&#xff1a;MAC子层&#xff08;帧头&…

MySQL表的增删改查(进阶)

目录1.数据库约束1.1约束类型1.2 NULL约束1.3 UNIQUE&#xff1a;唯一约束1.4 PRIMARY KEY: 主键约束1.5 DEFAULT&#xff1a;默认值约束1.6 FOREIGN KEY&#xff1a;外键约束2. 表的设计3. 新增4. 查询4.1 聚合查询4.1.2 GROUP BY子句4.2 联合查询4.2.1内连接4.2.2外连接4.2.3…

2022 第十四届蓝桥杯模拟赛第一期(题解与标程)

第十四届蓝桥杯模拟赛第一期1. 二进制位数问题描述答案提交参考答案2. 晨跑问题描述答案提交参考答案3. 调和级数问题描述答案提交参考答案程序验证4. 山谷问题描述答案提交参考答案5. 最小矩阵问题描述答案提交参考答案6. 核酸日期问题描述输入格式输出格式样例输入样例输出评…

项目风险管理的5大关键点,你做了几点?

1、全方位科学分析项目风险 为了提高项目抗风险能力&#xff0c;我们需要对项目风险进行科学全面的分析。一般我们从3个维度对风险进行科学分析&#xff1a;影响的严重性、发生的可能性、产生的影响性。 根据风险或机会对项目的影响程度&#xff0c;一般我们会从三个维度将其划…

javascript大作业《web课程设计》用html做一个期末作业网站,梅西足球体育网页,css

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

STING 与 cGAS的结合导致TBK1 激酶募集和活化

来自细菌或病毒的核酸在受感染的细胞中会产生强效的免疫反应&#xff0c;而病原体衍生核酸的检测是宿主感知感染并启动保护性免疫反应的核心策略。cGAS (Cyclic GMP-AMP synthase) 是一种双链 DNA 传感器&#xff0c;可催化 cGAMP&#xff08;cyclic GMP-AMP&#xff09;的合成…

二级导航栏

简介&#xff1a;本文通过HTML与CSS相集合的方式&#xff0c;来实现二级导航菜单。 HTML构建骨架 <body><ul class"nav1"><li>水果<ul class"nav2"><li>苹果</li><li>香梨</li><li>火龙果</li…

前端CSS射门动画-为梅西最后一届世界杯加油

☆ 距离2022卡塔尔世界杯只有6天时间了&#xff0c;众多球星我喜欢梅西和奥乔亚。 ☆ 我们不能到现场去&#xff0c;只能手中的代码自娱自乐一下&#xff0c;就当为梅西加油了。这是梅西最后一届世界杯了。 梅西给我的感觉&#xff0c;踢球足够利落干净&#xff0c;你不会从他的…

AI遮天传 ML/DL-感知机

感知机的出现是人工智能发展史一大重要里程碑&#xff0c;其后才诞生了&#xff1a;多层感知机、卷积神经网络等一系列的经典网络模型。 在我看来&#xff0c;它虽然是深度学习领域的一大开端&#xff0c;但本身解决的只是线性二分类问题&#xff0c;它本身与机器学习经典模型线…

RORγ 反向激动剂-XY101 小分子化合物

早在 2016 年&#xff0c;中国科学院广州生物医药与健康研究院许永教授团队就曾与加州大学戴维斯分校的陈宏武教授合作&#xff0c;首次发现核激素受体 RORγ 是作用于雄激素受体 AR 上游的关键驱动因子&#xff0c;直接调控雄激素受体 AR 的表达。因此&#xff0c;RORγ 成为前…

gitlab+jenkins+harbor次完整CI链条

用一台机器搭harbor 先安装docker工具&#xff0c;因为装harbor需要docker [rootharbor ~]# cd /etc/yum.repos.d [rootharbor yum.repos.d]# wget https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo [rootharbor yum.repos.d]# sed -i s#download.docke…

HTML做一个个人博客页面(纯html代码)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

【网页设计】期末大作业html+css (个人生活记录介绍网站)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

CNC程序管理NC文件版本管理CNC程序生命周期管理NC程序版本管理

CNC程序管理NC文件版本管理CNC程序生命周期管理NC程序版本管理 ROZRZ 数控程序文档流程管理系统能够实现 NC 数控程序文档及流程的管理&#xff0c;且为企业日后能够进行整体生产资料信息的综合管理做好了在程序代码方面的数据准备&#xff0c;提供了更为方便、快捷、安全的系…

Java——后端开发经验总结(持续更新中)

目录 一、判断实体类 二、图片无法正常显示 三、图片路径存储问题 四、xml注释 五、常用快捷键使用 每日三问 重要配置 一、判断实体类 Assert.notNull&#xff1a; 断言某个值是否为空 优点&#xff1a;告别了if判断为空。缺点&#xff1a;场景比较单一&#xff0c;基本…

【JavaSE】继承那些事儿

目录 1. 继承 1.1 为什么要有继承 1.2 何为继承 1.3 继承的语法 1.4 父类成员的访问 1.4.1 子类中访问父类的成员变量 1.4.2 子类中访问父类的成员方法 1.5 super 关键字 1.5.1 super 关键字能让子类访问父类同名成员变量及方法 1.5.2 super 关键字在子类构造方法所起的作用 1…

STC51单片机21——EEPROM测试

STC89C51RC系列单片机内部EEPROM详细地址表&#xff1a; 第一扇区 第二扇区 第三扇区 第四扇区 起始地址 结束地址 起始地址 结束地址 起始地址 结束地址 起始地址 结束地址 2000h 21FFh 22…

node.js的pat/fs/events模块以及磁盘遍历的实现

path模块: 概述: path模块是一个内置模块,他里面内置的相关方法来造作的路径地址 导入path模块: const path require(path) 使用方法: //导入对应的path模块 const pathrequire(path) //相关方法 //1.join方法是将多个相关的地址连接成一个地址 (做路径拼接) let pathStrpath…

2022年下半年系统架构设计师下午真题及答案解析

试题一(25分) 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。在项目立项之初&#xff0c;公司领导层一致认为本次升级的主要目标是提升会员管理方式的灵活性&#xff0c;由于当前用户规模不大&#xff0c;业务也相对…