程序地址空间

 引入

        看这样一段代码

  1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>4 5 int g_val = 100;6 int main()7 {8 pid_t id = fork();9 if(id==0)10 {11   int cnt = 0;12   while(1)13   {14     printf("child,pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);15     sleep(2);16     cnt++;17     if(cnt==5)18     {19       g_val = 200;                          20       printf("child change g_val:100->200\n");21     }22   }                                     23                        24 }                                                                                                                                                    25 else                                                               26 {                                                                27   while(1)                                                28   {                                   29     printf("father,pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);30     sleep(2);                                 31   }                                                                                                          32 }                     33   return 0;34 }

        运行结果为: 

        我们发现改之前父子进程的g_val的值都是100,更改之后child的g_val是100,father的g_val是200,但是&g_val的地址都是0x60105c,同一个地址怎么可能存储的值不同?

        原因:这个地址一定不是物理地址!这个地址是虚拟地址。


什么是进程地址空间?

进程地址空间的结构组成

栈区:自定义的变量,指针变量,反正是各种变量都是存在栈上,但是指针变量所指向的内容不一定在栈上。

例:

堆区:通过malloc/new申请的空间, 

静态区:(未初始化数据)(初始化数据)全局变量,静态变量。

例题:

int globalVar = 1;static int staticGlobalVar = 1;void Test(){static int staticVar = 1;int localVar = 1;int num1[10] = {1, 2, 3, 4};char char2[] = "abcd";char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof (int)*4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4);free (ptr1);free (ptr3);}第一组:选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)globalVar在哪里?__C__  staticGlobalVar在哪里?__C__staticVar在哪里?__C__  localVar在哪里?__A__num1 在哪里?__A__分析:globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区staticVar静态局部变量在静态区  localVar局部变量在栈区num1局部变量在栈区char2在哪里?__A__  *char2在哪里?__A__pChar3在哪里?__A__   *pChar3在哪里?__D__ptr1在哪里?__A__    *ptr1在哪里?__B__分析:char2局部变量在栈区  char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上pChar3局部变量在栈区   *pChar3得到的是字符串常量字符在代码段ptr1局部变量在栈区     *ptr1得到的是动态申请空间的数据在堆区第二组:sizeof(num1) = __40__;//数组大小,10个整形数据一共40字节sizeof(char2) = __5__;//包括\0的空间strlen(char2) = __4__;//不包括\0的长度sizeof(pChar3) = __4__;//pChar3为指针strlen(pChar3) = __4__;//字符串“abcd”的长度,不包括\0的长度sizeof(ptr1) = __4__;//ptr1是指针

进程地址空间的本质

        进程地址空间的本质是数据结构,具体到进程中就是特定的数据结构对象。

想象中:

 实际中:

        本质上就是进程地址空间就是一个个结构体连接,每个结构体被赋予了一个十六进制的地址,而进程的PCB属性中有指向进程地址结构体的指针

        每一个进程都存在一个进程地址空间(如上图),4GB(32位平台),操作系统的真实物理空间是4GB,操作系统给每个进程都“画了一张大饼”,给每个进程都分配了4GB的虚拟地址空间。


        如何理解分区?

        本质上还是结构体对数据区域进行了划分,结构体中的属性对分区的开始与结束进行了标记。


        我们的地址空间不具备对我们的代码和数据的保存的能力,它们是保存在物理内存空间的,将进程地址空间上的地址(虚拟地址)转化到物理内存中,进程提供一张映射表——页表

        这张表保存的是地址与地址之间的映射关系,CPU中的CR3通过映射关系来将虚拟地址映射到物理地址。


 引入中的问题

这样我们就可以理解一开始访问地址相同但是访问的数据不同的问题。

        原因就是进程之间具有独立性,访问的虚拟地址相同但是它们虚拟地址空间与物理地址空间直接产生映射的页表不同(映射关系不同),导致了访问地址相同但是访问的数据不同的问题。

以下用图来说明:

        一开始都是同样的映射关系,同一个虚拟地址对应了同一个地址空间。

        但是当子进程写入数据,这时发生了写时拷贝,复制一份数据并修改,这时虽然父和子的虚拟地址不同但是父和子的映射关系改变了,所以读到的数据不同。

为什么要有进程地址空间?(进程地址空间存在的意义)

1.将物理内存从无序变成有序。让进程统一的视角,看待内存。

        说明:对于进程在虚拟地址空间中的排布是十分清晰的(不同的数据在不同的分区有序排列),但是在物理空间中的排布是不得而知的,是混乱的,这时通过页表将虚拟地址空间与物理空间建立起联系,这时我们只用关注虚拟地址空间即可,至于物理空间交给操作系统通过页表进行操作。

2.将进程管理和内存管理进行解耦合。

        说明:OS对物理空间进行管理,进程对自己的虚拟地址空间进行管理,而物理空间和进程地址空间通过一张页表进行映射,这样进程就不会对物理空间进行干扰,它们之间互不干扰!

3.地址空间+页表是保护内存安全的重要手段!
        说明:当进行进行非法操作物理内存的其他数据时,操作系统会进行拦截,这时仅在虚拟地址空间进行了错误的访问修改,而不会在物理空间中进行。例如:野指针进行非法的越界访问修改时进程可能会崩溃,而操作系统不会,原因是操作系统将这种操作拦截,这种操作仅是在进程地址空间中,而不会在物理空间中。

        这也说明了进程之间是具有独立性的,一个进程的崩溃不会影响其他进程。 

解释一些事情:

malloc/new申请内存

1.申请的内存,本质是在哪里申请的?

        进程的虚拟地址空间中申请。

2.申请的内存会直接使用吗?

        不一定,申请了的内存只是虚拟地址空间,当要写入时,操作系统先进行阻拦(缺页中断),去建立虚拟地址空间和物理地址空间的映射放入页表(真正的申请空间),这时操作系统放开阻拦,允许写入。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

这可能是最全的Web测试各个测试点,有这一篇就够了

前言 什么是Web测试&#xff1f; Web测试测试Web或Web应用程序的潜在错误。它是在上线前对基于网络的应用程序进行完整的测试。 Web测试检查 功能测试 易用性测试 接口测试 性能测试 安全测试 兼容性测试 1、功能测试 测试网页中的所有链接、数据库连接、网页中用于提交或从…

[C语言]——分支和循环(1)

目录 一.if语句 1.if 2.else 3.分支中包含多条语句 4.嵌套if 5.悬空else问题 二.关系操作符 三.条件操作符 C语⾔是结构化的程序设计语⾔&#xff0c;这⾥的结构指的是顺序结构、选择结构、循环结构&#xff0c;C语⾔是能够实现这三种结构的&#xff0c;其实我们如果仔细分析&a…

2024年6个最佳WordPress游戏化插件

在寻找最好的WordPress游戏化插件来提高网站的参与度&#xff1f; WordPress游戏化是将游戏元素应用到WordPress 网站的想法&#xff0c;例如得分、排行榜、奖项、测验结果共享等。 显然&#xff0c;WordPress 的核心并不包含这些类型的功能。借助WordPress游戏化插件&#x…

Windows下CMake使用PCL提示全局作用域没有_open等文件读写函数

表现 解决办法 在导入PCL之前导入Windows SDK相关头文件: #if _WIN32 #include <corecrt_io.h> #endif

【Web】浅浅地聊SnakeYaml反序列化两条常见利用链

目录 关于Yaml 关于SnakeYaml SnakeYaml反序列化利用 JdbcRowSetImpl链 ScriptEngineManager链 复现 基本原理 继续深入 关于Yaml 学过SpringBoot开发的师傅都知道&#xff0c;YAML和 Properties 文件都是常见的配置文件格式&#xff0c;用于存储键值对数据。 这里举…

DHCP部署与安全

在当今快速发展的网络世界中&#xff0c;动态主机配置协议&#xff08;DHCP&#xff09;扮演着至关重要的角色。这项技术不仅简化了网络管理&#xff0c;还提高了网络资源的利用率。本文旨在深入探讨DHCP的工作原理、优势以及如何有效部署和保护DHCP服务器。 一、DHCP作用 自…

微软亚太区AI智能应用创新业务负责人许豪,将出席“ISIG-AIGC技术与应用发展峰会”

3月16日&#xff0c;第四届「ISIG中国产业智能大会」将在上海中庚聚龙酒店拉开序幕。本届大会由苏州市金融科技协会指导&#xff0c;企智未来科技&#xff08;AIGC开放社区、RPA中国、LowCode低码时代&#xff09;主办。大会旨在聚合每一位产业成员的力量&#xff0c;深入探索A…

dolphinescheduler调用API

&#xff08;作者&#xff1a;陈玓玏&#xff09; 1. 打开api文档 api文档地址&#xff1a;http://{api server ip}:12345/dolphinscheduler/swagger-ui/index.html?languagezh_CN&langcn&#xff0c;我是用k8s部署的&#xff0c;所以ip和端口是由service决定的&…

接口测试工具Postman接口测试图文教程

一、前言 在前后端分离开发时&#xff0c;后端工作人员完成系统接口开发后&#xff0c;需要与前端人员对接&#xff0c;测试调试接口&#xff0c;验证接口的正确性可用性。而这要求前端开发进度和后端进度保持基本一致&#xff0c;任何一方的进度跟不上&#xff0c;都无法及时…

数学界高层犯了低级错误

以前根本不懂“算术代数几何”&#xff0c;认为似乎高不可攀&#xff0c;但早就觉得代数里面 adele 和 idele 的理论是纯概念的&#xff0c;没有技术含量&#xff0c;不可能用来解决数学问题。但当哈工大女研究生张扬讲&#xff08;她在北京听讲座期间&#xff09;&#xff0c;…

聚焦两会 | 从2024年政府工作报告看网络安全新机

在今年的《政府工作报告》&#xff08;下面简称“报告”&#xff09;中&#xff0c;除了对2023年里我国所取得的重大成就作了全面总结外&#xff0c;针对2024年全年经济社会发展作出的部署安排引起全国人民的关注。报告中与网络安全相关的内容也引起网络安全行业相关从事人员的…

事务 失效的八种情况

一、非public修饰的方法 Transactional注解只能在在public修饰的方法下使用。 /*** 私有方法上的注解&#xff0c;不生效&#xff08;因私有方法Spring扫描不到该方法&#xff0c;所以无法生成代理&#xff09;*/ Transactional private boolean test() {//test code } 非publ…