虚拟地址空间
本节目录
- 1.如何理解区域划分
- 2.为什么一个变量可以存储两个不同的值?
- 3.深入理解虚拟地址空间
- 为什么要有地址空间?
 
- 4.理解什么是挂起?
1.虚拟地址空间究竟是什么?
2.映射关系的维护是谁做的?
1.如何理解区域划分
所谓的区域划分,本质是在一个范围里定义start和end。
struct destop
{int start;int end;
};struct destop one = { 1, 50 };
struct destop two = { 51, 100 };
虚拟地址空间是一种内核数据结构,它里面至少要有对各个区域的划分。
struct addr_room
{int code_start;int code_end;int init_start;int init_end;int uninit_start;int uninit_end;int heap_start;int heap_end;int stack_start;int stack_end; //... 其他的属性
};
地址空间和页表(用户级)是每个进程都私有一份。(页表是一种数据结构,它内部有映射关系,也有权限的管理,+MMU)
 只要保证每一个进程的页表映射的是物理内存的不同区域,就能做到进程之间不会互相干扰,进而保证进程的独立性。
2.为什么一个变量可以存储两个不同的值?
首先我们观察下面一段代码:
  1 #include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 5 int g_val = 0;6 int main()7 {8   pid_t id = fork();9   if(id <0)10   {11     perror("fork");12     return 0;13   }14   else if(id == 0)15   {16     printf("child[%d]:%d,%p\n",getpid(),g_val,&g_val);17   }18   else19   {20     printf("parent[%d]:%d,%p\n",getpid(),g_val,&g_val);                                                                                                                                                      21   }22   return 0;23 }运行结果:
[jyf@VM-12-14-centos lesson9]$ ./a.out
parent[4479]:0,0x601050
child[4480]:0,0x601050我们发现变量值和变量地址是一模一样的,很好理解呀,因为子进程是以父进程为模板,父进程并没有对变量值做任何修改。
 我们在将代码进行修改:
  1 #include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 5 int g_val = 0;6 int main()7 {8   pid_t id = fork();9   if(id <0)10   {11     perror("fork");12     return 0;13   }14   else if(id == 0)15   { //子进程肯定先跑完,也就是子进程先修改,完成之后,父进程在读取16     g_val = 100;17     printf("child[%d]:%d,%p\n",getpid(),g_val,&g_val);18   }19   else //parent 20   {21     sleep(3);22     printf("parent[%d]:%d,%p\n",getpid(),g_val,&g_val);23   }24   sleep(1);                                                                                                                                                                                                  25   return 0;                                                                                                                                                                        26 }              
运行结果:
[jyf@VM-12-14-centos lesson9]$ ./a.out
child[6691]:100,0x601058
parent[6690]:0,0x601058我们发现父子进程,输出的变量地址是一样的,但变量的值不同。
一个变量可以存储两个不同的值吗?答案是否定的。输出的变量值不同说明它们绝对不是同一个变量,但输出的地址相同,说明它们不是物理地址,在Linux环境下,它叫做虚拟地址,我们在C/C++下,看到的都是虚拟地址,物理地址用户一概看不到,由OS统一管理,将虚拟地址映射成物理地址。进程地址空间是一种内核的数据结构,每一个进程都私有一份进程地址空间和页表,只要保证进程虚拟地址通过页表映射的是物理内存的不同区域,就能做到进程之间不会互相干扰,保证进程的独立性。所以父子进程的g_val的虚拟地址是一样的,当子进程修改g_val的值,通过页表映射到物理内存的不同区域,就出现了类似一个变量内存储两个不同的值的现象。
如此pid_t id = fork(); 中的id值为什么可以有两个返回值相信大家都能理解了。

3.深入理解虚拟地址空间
一些Linux指令:readelf -s 可执行文件, 用于查看可执行程序中的符号表。
 objdump -f 可执行文件,用于查看可执行程序中文件头相关信息,一个反汇编工具。
[jyf@VM-12-14-centos lesson9]$ objdump -f a.outa.out:     file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400560当我们的程序通过编译形成可执行程序的时候,,还没有加载到内存的时候,请问我们程序的内部有地址吗?
 可执行程序在编译的时候,内部其实就已经有地址了。
 地址空间不要仅仅理解为OS要遵守的,编译器也要遵守。即编译器在编译代码的时候,就已经给我们形成了各个区域,代码区,数据区等等,并且,采用Linux一样的编制方式,给每一个变量,每一个代码都进行了编址,故,程序在编译的时候,每一个字段早已经具有了一个虚拟地址!!!
 程序内部的地址,依旧用的是编译器编译好的虚拟地址,当程序加载到内存的时候,每行代码,每个变量便具有了一个外部的物理地址。
 所以CPU读取到指令的时候,指令内部的地址是虚拟地址。可见,每一个变量,每一个函数都有地址,编译器给的,同样它们也一定被加载到物理内存中。

为什么要有地址空间?
1.凡是非法访问或者映射,OS都会识别到,并终止这个进程。这样可以做到有效保护内存。
 我们知道地址空间和页表都是OS创建并维护的,是不是就意味着凡是使用地址空间和页表进行映射,也一定要在OS的监管之下进行访问,也便保护了物理内存中的所有合法数据,包括各个进程,以及内核的相关有效数据。
2.因为有地址空间和页表的存在,是不是我们的物理内存可以对未来的数据进行任意位置的加载?
 当然可以。
 这样物理内存的分配和进程的管理就可以做到没有关系,这样内存管理模块和进程管理模块就完成了解耦合。
 从这我们可知,空间配置器配置的地址是虚拟地址,我们在C/C++中new/malloc申请空间的时候,本质是在虚拟地址空间进行申请。
如果我申请了物理空间,但我不立马使用,是不是空间的浪费呢?是的。
本质上,因为地址空间的存在,所以上层申请空间,其实是在地址空间上进行申请,物理内存甚至一个字节都不给你!!!而当你真正进行物理地址空间的访问的时候,才执行内存的相关管理算法,帮你申请内存,构建页表映射关系(是由OS自动帮你完成的,用户包括进程完全0感知),然后在让你进行内存的访问。这种延迟分配的策略,来提高整机的效率,几乎内存的有效使用是100%的。这也就是**缺页中断技术。**也就是说,你申请空间,系统已经给你分配了虚拟地址空间,但还没有给你分配物理地址,这种技术叫做缺页中断技术。
3.因为在物理内存上,理论可以任意位置加载,所以物理内存中几乎所有数据和代码都是乱序的。
 但是,因为页表的存在,它可以将页表中的虚拟地址和物理地址进行映射,那么可见在进程的视角下,所有的内存分布,都可以是有序的。
 地址空间加页表的存在可以将内存分布有序化。
 地址空间是操作系统给进程画的大饼。进程要访问物理内存中的数据和代码,可能此时并不在物理内存中。同样的也可以让不同的进程映射不同的物理内存区域,是不是很容易做到,进程独立性的实现。(进程的独立性可以通过地址空间+页表实现)
 因为地址空间的存在,每一个进程都认为自己拥有4GB(32)空间,并且各个区域都是有序的,进而可以通过页表映射到物理内存不同区域,来实现进程的独立性。每一个进程都不知道,也不需要知道其他进程的存在。
4.理解什么是挂起?
我们将我们的代码和数据加载到内存中,加载本质是创建进程,那么是不是非得立马把所有的程序的代码和数据加载到内存中,并创建内核数据结构和建立相关映射关系?答案是否定的。
 在最极端的情况下,甚至只有内核结构(task_struct结构体、地址空间、页表,)被创建出来了,映射关系一个都没有。
 此时进程的状态就叫做新建状态。当真正调度这个进程的时候,才将进程的代码和数据加载到内存中,设置好映射关系。
理论上,可以实现对进程的分批加载,那是不是可以分批换出呢?当然可以。甚至,这个进程短时间内不会被执行,比如正在等待某种非CPU资源,阻塞了,进程的代码和数据被换出,此时的状态叫做挂起!!!
 我们知道页表映射的时候,可不仅仅是内存,磁盘中的位置也是可以映射的嗷,所以在执行换出操作时,不一定需要将内存中数据刷新到磁盘中,可以直接释放掉,只需要将页表的映射关系中的位置改到磁盘中对应的数据位置即可。
今天就到这里吧,我们下次再见!
相关文章:
 
虚拟地址空间
本节目录1.如何理解区域划分2.为什么一个变量可以存储两个不同的值?3.深入理解虚拟地址空间为什么要有地址空间?4.理解什么是挂起?1.虚拟地址空间究竟是什么?2.映射关系的维护是谁做的?1.如何理解区域划分 所谓的区域…...
 
Python基础篇(十五)-- Pygame游戏编程
1 初识Pygame Pygame是一个开源的Python模块,专门用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在SDL的基础上,SDL是一套跨平台的多媒体开发库,用C语言实…...
 
LeetCode 热题 HOT 100 Java 题解 -- Part 2
练习地址 Part 1 : https://blog.csdn.net/qq_41080854/article/details/128829494 LeetCode 热题 HOT 100 Java 题解 -- Part 236. 二叉树的中序遍历 9437. 不同的二叉搜索树 9638. 验证二叉搜索树 9839. 对称二叉树 10140. 二叉树的层序遍历 10241. 二叉树的最大深度 10442.…...
 
【项目实战】IDEA常用快捷键汇总
一、修改为Eclipse的快捷键 相信很多朋友跟我一样, 都是习惯了eclipse的快捷键,没错,习惯这东西真的很难改!IDEA非常强大,支持我们修改IDEA中的keymap为Eclipse的快捷键!友好又贴心,有没有&…...
 
更新 TKK 失败,请检查网络连接。谷歌翻译 translation插件不能用解决办法 亲测有效
谷歌翻译无法使用,谷歌回应解释是,谷歌翻译使用率过低,所以选择停止服务。网上也有说法,指出根本原因为,提供API接口的googleapis被墙,这导致js文件和字体资源无法加载。 这里提供两种解决办法 方案一 修…...
 
SpringBoot整合MybatisPlus多数据源
相信在很多使用MybatisPlus框架的小伙伴都会遇到多数据源的配置问题,并且官网也给出了推荐使用多数据源 (dynamic-datasource-spring-boot-starter) 组件来实现。由于最近项目也在使用这个组件来实现多数据源切换,因此想了解一下该组件是如何运行的&…...
【教程】如何使用Java生成PDF文档?
在如今数字化时代,越来越多的人使用PDF文档进行信息传递和共享。而使用Java生成PDF文档也成为了一个非常重要的技能,因为Java作为一种通用的编程语言,可以在不同的操作系统和平台上运行。下面,我们将为您介绍如何使用Java生成PDF文…...
 
I.MX6ULL内核开发13:pinctrl子系统和gpio子系统-led实验
目录 一、pinctrl子系统 1.1 pinctrl子系统编写格式以及引脚属性介绍 1.1.1 iomux节点介绍 1.1.2 pinctrl子节点编写格式 1.1.3 引脚配置信息介绍 1.2 将RGB灯引脚添加到pinctrl子系统 1.2.1 查找RGB灯使用的引脚 1.2.2找到引脚宏定义 1.2.3 设置引脚属性 1.2.4 在…...
 
Linux系列 使用vi文本编辑器
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.vi文本编辑器 1.使用vi文本编辑器 2.vi编辑器的工作模式 3.命令模式中的…...
 
【java基础】接口(interface)
文章目录基础介绍接口的定义关于接口字段和方法的说明使用接口抽象类和接口接口方法冲突的一些说明方法相同名称和参数,返回值相同方法名称相同,参数不同,返回值相同方法返回值不同,名称参数相同方法完全相同,一个有默…...
 
ChatGPT(GPT3.5) OpenAI官方API正式发布
OpenAI社区今天凌晨4点多发送的邮件,介绍了ChatGPT官方API的发布。官方介绍文档地址为“OpenAI API”和“OpenAI API”。 ChatGPT(GPT3.5)官方API模型名称为“gpt-3.5-turbo”和“gpt-3.5-turbo-0301”。API调用价格比GPT text-davinci-003模型便宜10倍。调用费用为…...
 
CAD中如何将图形对象转换为三维实体?
有些小伙伴在CAD绘制完图纸后,想要将图纸中的某些图形对象转换成三维实体,但却不知道该如何操作,其实很简单,本节CAD绘图教程就和小编一起来了解一下浩辰CAD软件中将符合条件的对象转换为三维实体的相关操作步骤吧! 将…...
 
【K8S笔记】Kubernetes 集群架构与组件介绍
K8S 官方文档 https://kubernetes.io/zh/docs/home ##注重关注 概念和任务 板块。 K8S 集群架构 K8S也是运用了分布式集群架构: 管理节点/Master 整个集群的管理,任务协作。工作节点/Node 容器运行、删除。 K8S 组件介绍 管理节点/Master 相关组件 …...
 
9 怎么登录VNC
1)首先在ssh登录后启动vncserver。登陆后输入下面的指令来创建自己的VNC。 命令vncserver :16 –geometry 1900x1000 其中:16是分配的端口号,1900x1000是分辨率。如果没有响应,建议执行下面操作后再次重复上面操作。 命令…...
 
MPI ubuntu安装,mpicc,mpicxx,mpif90的区别
介绍 MPI是并行计算的一个支持库,支持对C、C、fortran语言进行并行计算。 安装基础环境 ubuntu进行gcc/g/gfortran的安装: gcc: ubuntu下自带gcc编译器。可以通过gcc -v命令来查看是否安装。 g: sudo apt-get install buil…...
 
移动端笔记
目录 一、移动端基础 二、视口 三、二倍图/多倍图 四、移动端开发 (一)开发选择 (二)常见布局 (三)移动端技术解决方案 五、移动WEB开发之flex布局 六、移动WEB开发之rem适配布局 #END(…...
 
操作系统笔记、面试八股(一)—— 进程、线程、协程
文章目录1. 进程、线程、协程1.1 进程1.1.1 进程间的通信方式1.1.2 进程同步方式1.1.3 进程的调度算法1.1.4 优先级反转1.1.5 进程状态1.1.6 PCB进程控制块1.1.7 进程的创建和撤销过程1.1.8 为什么要有进程1.2 线程1.2.1 为什么要有线程1.2.2 线程间的同步方式1.3 协程1.3.1 什…...
 
Python每日一练(20230302)
目录 1. 字符串统计 2. 合并两个有序链表 3. 下一个排列 附录 Python字典内置方法 增 删 改 查 其它 1. 字符串统计 从键盘输入一个包含有英文字母、数字、空格和其它字符的字符串,并分别实现下面的功能:统计字符串中出现2次的英文字母&#…...
Numpy课后练习
Numpy课后练习 文章目录 Numpy课后练习一、前言二、题目及答案一、前言 答案仅供参考,谢谢大家! 二、题目及答案 导入Numpy包并设置随机数种子为666 import numpy as np np.random.seed(666)创建并输出一个包含12个元素的随机整数数组r1,元素的取值范围在[30,100)之间 r1 …...
动态规划dp中的子序列、子数组问题总结
目录 定义dp数组 初始化dp数组 状态转移方程 最终结果 题目 定义dp数组 这类问题的共性是会提供两个数组,寻找他们共同的子序列、子数组。设第一个数组为s,第二个数组为t。则可以设二维dp数组,其大小为len(s + 1)*len(t + 1) dp[i][j]表示 s 前 i 个长度,...
 
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
 
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
 
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
 
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
 
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
