当前位置: 首页 > news >正文

【Linux杂货铺】3.程序地址空间

1.程序地址空间的引入

        fork()函数在调用的时候子如果是子进程则返回0,如果是父进程则返回子进程的pid,在代码中我们分别在子进程和父进程读取全局变量g_val的时候居然出现了俩个不同的值。如下:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_val = 0;
int main()
{pid_t id = fork();if(id == 0){g_val = 100int i = 5;while(i--){printf("我是子进程,g_val值为:%d,g_val的地址:%p\n",g_val,&g_val);sleep(1);}}else{int i=5;while(i--){printf("我是父进程,g_val值为:%d,g_val的地址为:%p\n",g_val,&g_val);sleep(1);}}return 0;                                                                                                                        }

执行结果: 

[xvjiyi@hecs-399428 20240712_pragramAddressSpace]$ ./mytest
我是父进程,g_val值为:0,g_val的地址为:0x7ffd52f7b964
我是子进程,g_val值为:100,g_val的地址为:0x7ffd52f7b964
我是父进程,g_val值为:0,g_val的地址为:0x7ffd52f7b964
我是子进程,g_val值为:100,g_val的地址为:0x7ffd52f7b964
我是父进程,g_val值为:0,g_val的地址为:0x7ffd52f7b964
我是子进程,g_val值为:100,g_val的地址为:0x7ffd52f7b964
我是父进程,g_val值为:0,g_val的地址为:0x7ffd52f7b964
我是子进程,g_val值为:100,g_val的地址为:0x7ffd52f7b964
我是父进程,g_val值为:0,g_val的地址为:0x7ffd52f7b964
我是子进程,g_val值为:100,g_val的地址为:0x7ffd52f7b964

         这个时候我们发现明明读取的地址相同为什么会读出俩个不同的值呢?既然读出了俩个值,说明这俩个值存在不同空间里,定然不可能地址相同,唯一的解释就是这个地址不是真实的地址。事实也正是这样,这里读取到的地址是虚拟地址,虚拟地址空间也就是程序地址空间的产物,接下来我们来看程序地址空间,以及虚拟地址的实现机制。

2.程序地址空间

2.1虚拟地址空间介绍

        程序地址空间本质是一个结构体(我们认为是 struct mm_struct),这个结构体记录着不同内存区间的在这个程序地址空间的相对地址(不是真正的物理地址),映射关系如下:

2.2页表

        当进程访问地址的时候访问的是虚拟地址,那我们最终是怎样访问到物理地址的呢?当进程地址空间创立时,操作系统会建立一张页表完成虚拟地址到物理地址的映射,如下:

2.3 子进程父进程访问全局变量出现俩个不同值 

         接下来我们回到开头的问题,为什么我们会在同一个地址上访问到俩个不同的值。这是因为子进程继承了父进程的地址空间,父子进程在访问这个全局变量时访问的虚拟地址是一致的,而这俩个虚拟地址映射的物理地址不同,而我们的数据是存放在物理内存中的,也就造成了同一个地址出现了俩个不同值的假象。(至于为什么会存放在不同的物理内存中在下一节进程控制中给大家讲解,为了确保进程的独立性相互之间不影响,发生了写时拷贝)。

3.扩展

3.1为什么要有地址空间

      如果没有进程地址空间,物理内存就可以被随意访问,可以随意改动不属于自己的空间,系统安全性大大降低。我们有了地址空间有以下优点:

  1. 1.当进程访问到不属于该进程的空间时,操作系统可在页表映射时拒绝进程的访问,有效杜绝了物理地址被随意访问,大大保证了物理内存和其他进程的安全。
  2. 2.将进程管理与内存管理解耦合。
  3. 3.让进程以统一的视角看待自己的代码和数据。

3.2 malloc申请空间的实质

        当进程使用malloc申请空间时,在访问页表的时候操作系统不会给你立马分配物理地址,而会发生缺页中断,在进程需要访问这块空间时才给你分配空间。

3.3程序的加载

        程序编译在加载的时候并不是一股脑全部加载到物理内存中,而是执行多少加载多少,一边执行一边加载。(源代码在被编译时早就将源代码和数据按照程序地址空间的方式完成了编址)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • UART编程
  • 基于复旦微JFMQL100TAI的全国产化FPGA+AI人工智能异构计算平台,兼容XC7Z045-2FFG900I
  • 全面揭秘:ChatGPT-4o带来的下一代AI能力
  • 环境管理开发实战
  • 卸载docker
  • Python input NameError: name ‘xxx‘ is not defined.
  • 智充科技营收增速放缓:经营成本飙升,应收账款大幅增长
  • Halcon机器视觉15种缺陷检测案例_4产品毛剌检测
  • 【2024年全国青少信息素养大赛c++初中复赛集训第一天编程题分享】
  • 3、Chronos
  • 数学建模·模糊评价法
  • ffmpeg新旧函数对比
  • 微信视频号的视频怎么下载到本地?快速教你下载视频号视频
  • 算法训练营day11 栈与队列(栈的应用,单调队列,优先队列)
  • SSRF漏洞深入利用与防御方案绕过技巧
  • CSS 三角实现
  • httpie使用详解
  • Nodejs和JavaWeb协助开发
  • python_bomb----数据类型总结
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 从0到1:PostCSS 插件开发最佳实践
  • 基于遗传算法的优化问题求解
  • 前端js -- this指向总结。
  • 责任链模式的两种实现
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ‌JavaScript 数据类型转换
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • (C语言)二分查找 超详细
  • (pytorch进阶之路)扩散概率模型
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (每日一问)计算机网络:浏览器输入一个地址到跳出网页这个过程中发生了哪些事情?(废话少说版)
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (五)MySQL的备份及恢复
  • (一) springboot详细介绍
  • .env.development、.env.production、.env.staging
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .net core 外观者设计模式 实现,多种支付选择
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .Net(C#)常用转换byte转uint32、byte转float等
  • @antv/x6 利用interacting方法来设置禁止结点移动的方法实现。
  • @ConfigurationProperties注解对数据的自动封装
  • [.NET 即时通信SignalR] 认识SignalR (一)
  • [AHK] WinHttpRequest.5.1报错 0x80092004 找不到对象或属性
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [C++初阶]vector的初步理解
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [CDOJ 1343] 卿学姐失恋了
  • [Codeforces] combinatorics (R1600) Part.2
  • [Datawhale AI夏令营 2024 第四期] 从零入门大模型微调之旅的总结
  • [FFmpeg] windows下安装带gpu加速的ffmpeg
  • [flask]http请求//获取请求体数据
  • [Gamma]阶段测试报告