虚拟地址空间【详解】 虚拟地址空间是什么 | 为什么要有虚拟地址空间

article/2025/9/30 8:33:23

目录

一、什么是虚拟地址空间 / 虚拟地址空间是如何被设计的

1.先看一下linux空间分布

I.示意图:

 II.验证: 

2. 在已知Linux内存分布之后,我们来看一个奇怪的现象

I.代码 :

II. 输出:

III.思考,引出--虚拟内存(虚拟地址空间):

 IV.虚拟内存(虚拟地址空间)

V.用 虚拟内存(虚拟地址空间) 来 解释 刚才 fork()产生的 一个变量两个值 的问题

二、为什么要有地址空间


一、什么是虚拟地址空间 / 虚拟地址空间是如何被设计的

1.先看一下linux空间分布

I.示意图:

 II.验证: 

myhello:myhello.cgcc -o myhello myhello.c//linux小技能://myhello:hello1.c hello2.c hello3.c hello4.c//gcc -o $@ $^//可以把 hello1.c hello2.c hello3.c hello4.c一次性全部gcc 成可执行程序
.PHNOY: clean
clean:rm hello1.c hello2.c hello3.c hello4.c

//myhello.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int g_unval;
int g_val = 100;int main(int argc, char* argv[], char* env[])
//int argc是命令行选项的个数,
//char *argv[]是用来存 该可执行程序的文件名和其对应的操作选项的,如ls -a -l 
//char *env[]是用来接收环境变量的指针数组,因为环境变量基本是字符串,而且有多个,所以用指针数组存{// int a = 10;//字面常量const char* str = "helloworld";// 10;// 'a';printf("code addr: %p\n", main);printf("init global addr: %p\n", &g_val);printf("uninit global addr: %p\n", &g_unval);static int test = 10;char* heap_mem = (char*)malloc(10);char* heap_mem1 = (char*)malloc(10);char* heap_mem2 = (char*)malloc(10);char* heap_mem3 = (char*)malloc(10);printf("heap addr: %p\n", heap_mem); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem1); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem2); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem3); //heap_mem(0), &heap_mem(1)printf("test stack addr: %p\n", &test); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem1); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem2); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem3); //heap_mem(0), &heap_mem(1)printf("read only string addr: %p\n", str);for (int i = 0; i < argc; i++){printf("argv[%d]: %p\n", i, argv[i]);}for (int i = 0; env[i]; i++){printf("env[%d]: %p\n", i, env[i]);}return 0;
}

ps:

1.堆是向上增长的

2.栈是向下增长的

2. 在已知Linux内存分布之后,我们来看一个奇怪的现象

I.代码 :

//makefile
mytest:test.cgcc test.c -o mytest 
.PHONY: clean
clean:rm -f mytest        
    //test.c1 #include <stdio.h>                                                                                                                                                   2 #include <unistd.h>3 int a=100;4 int main()5 {6   pid_t pid =fork();7   if(pid<0)8   {9     sleep(1);10     while(1)11     {12     printf("error\n");13     14     }15   }16   if(pid==0)17   {18 19     while(1)20     {21       sleep(1);22       a=200;
E> 23       pintf("i am child,pid:%d,ppid:%d,&a:%p,a:%d\n",getpid(),getppid(),&a,a);24     }25   }26   if(pid>0) 27   {28     while(1)29     {30     sleep(1);31       a=500;32       printf("i am father,pid:%d,ppid:%d,&a:%p,a:%d\n",getpid(),getppid(),&a,a);33     }34   }35   return 0;36 }   

II. 输出:

III.思考,引出--虚拟内存(虚拟地址空间):

疑问:为什么一个变量会有两个值呢?我们理解的物理内存上的同一个变量绝对不可能一个变量有两个值的。那么只有一个可能,这个变量并不是在我们所理解的物理内存上存储的。

答案 :是的,不仅是Linux上这个变量a,而且以往,我们所有在C/C++中所学的变量,基本都是不是存储在物理内存上的,而是存储在 虚拟内存 上。----所以刚才我们展示的Linux内存分布图,其实也是其 虚拟内存 的分布图

                Linux 的虚拟内存的分布图

 IV.虚拟内存(虚拟地址空间)

接下来浅浅看下图,了解 虚拟内存 大致的位置

图文解释:

1.TCB--进程结构体,负责存储进程的属性信息等;

2.struct address_room--虚拟地址空间内核结构体;

3.页表--用于映射的结构体

(以上三种是操作系统中常见的三种结构体,其中struct address_room 和 页表 共同形成一个 具有访问条件判断 映射

过程:

TCB结构体在进入CPU的运行队列之前(在源程序编译阶段就已经存在了虚拟地址,因为编译阶段需要调用各种函数)就已经将对应的 虚拟地址虚拟地址的的映射关系 存在了虚拟地址空间 、页表 、和 物理内存 中,形成了对应的映射,并且这种映射 是有访问的条件 判断的。每当要合法的访问某个变量时,系统都会去虚拟内存里找对应的映射关系去访问物理内存。

V.用 虚拟内存(虚拟地址空间) 来 解释 刚才 fork()产生的 一个变量两个值 的问题

需知 

我们要知道的是,在操作系统中, 页表 和 虚拟地址空间 是不止一对的,因为进程很多,每个进程都可以有 页表 和虚拟地址空间。

初步解释 

所以刚才的 由于fork();之后所产生的一个变量两个值的问题,其实是由于父进程和子进程都有自己的 页表 和 虚拟地址 空间的问题

 在深入理解之前,我们先简单了解一下 写时拷贝

 

 写时拷贝的作用/优点:

1.写时拷贝使得两个指向同一块空间的指针或迭代器或寄存器 得以彻底分离,保证了进程的独立性

2.是一周延时申请空间的技术,因为只有在真正需要用的时候才会去开辟空间,减少了开辟空间不使用的情况,所以--可以提高整机的使用率

 深入解释 

图文解释:

pid:1.因为fork() 结束之后,return 回的值给pid_t pid前,子进程的所有属性和信息都是仿照了父进程复制过去的,所以父进程中  a的虚拟地址 也和 子进程中 a的虚拟地址 是一样的,所以通过页表在 物理内存中的映射的a也是同一个;2.在fork()结束后,return回值给pid_t pid时,pid_t pid会因为写时拷贝,父进程中的pid和子进程中的pid会指向不同的物理内存,使得其对应的pid是独立的,也使得其对应的pid的值可以是不同的。

a: 同理,a在未被修改值之前,父进程和子进程的a的虚拟地址是相同的,也是指向同一块物理内存的;a在被修改值之后,父进程和子进程之后的a的虚拟地址虽然相同,但是子进程中的a已经因为写时拷贝的原因,重新开辟了一个空间了,使得父进程中和父进程中的a是独立的不同的两个a,所以其两个a的值也可以是不同的。

 写时拷贝之后

 OK,通过以上的 图解 和 例子,我们已经对虚拟地址空间有了较深的理解,

那么虚拟地址空间到底是什么呢?总结:是一种看待内存的方法,是一种数据结构。

那么虚拟地址空间是如何设计的呢? 总结: 将物理地址 在 虚拟地址空间中 描述起来,再用虚拟地址空间和页表来管理。

二、为什么要有地址空间

a. 当进程 非法的 访问或者映射, OS都会识别到并且终止该进程。

详解:比如所有的进程崩溃,就是进程的退出 ———》实质上是操作系统管理进程,将进程杀掉,让进程退出——》对用户的非法访问进行了有效的拦截,有效的保护了物理内存——》保护了物理内存中所有合法的数据-包括 各个进程,以及内核的相关有效数据(因为地址空间和页表是 OS 创建的,所以想使用虚拟地址空间和页表进行映射,也一定要在OS的监督之下来进行访问)

b.任意位置处,都可以用虚拟地址+页表解耦用来提高其他进程之间的独立性

详解:

1.任意位置处加载:我们的物理内存中 ,可以对未来的数据进行任意位置加载。只要映射能找到,物理内存的 就可以 进程管理 做到没有关系。(额外一个小知识:malloc在开辟空间时会预留“饼干空间”来储存开辟的空间的属性,映射就是通过“饼干空间”中的开辟内存的属性来进行对应的映射的)

2.解耦:因为虚拟地址+页表可以做到任意位置处加载,使得 内存管理模块 和进 程管理模块 无关联 就完成了解耦合。(解耦合-位置关联性降低,在映射的时候不需要对应的映射区域,减少模块和模块之间的关联性。耦合度越低,维护成本越低。和模块化函数之后方便维护是一个道理)

c.地址空间中有写时拷贝,可以延迟分配,用来提高整机效率 

详解: 

写时拷贝,延迟分配,可以做到,以足够内存的视角来执行进程,并且在使用时再开辟,减少了开辟了不及时使用,而增加系统内耗,浪费系统资源的可能。

d.可以让进程以统一的独占整个内存(实际上不是,只是以这种视角)的视角,映射到任意不同的物理内存的位置,来完成进程独立性的实现


http://chatgpt.dhexx.cn/article/mdbw6jW9.shtml

相关文章

虚拟地址转物理地址

概述 CPU是如何利用页目录和页表 等数据结构将一个32位的虚拟地址翻译为32位的物理地址的。其过程可以概括为如下步骤。 ① 通过CR3寄存器定位到页目录的起始地址&#xff0c;正因如此&#xff0c;CR3寄存器又称为页目录基地址寄存器&#xff08;PDBR&#xff09;。取线性地址…

虚拟地址与物理地址

虚拟地址 恰如其名&#xff0c;这个地址是虚拟的&#xff0c;与具体环境是解耦的&#xff0c;这样可以是程序员在编写程序时只需要关注代码逻辑本身&#xff0c;而不需要考虑地址分配。这些虚拟地址并不是真正运行在机器上的内存地址&#xff0c;每个程序的虚拟地址都是独立存在…

虚拟机ip地址

文章仅为自己在建立虚拟机过程中遇到的问题进行记录。 虚拟机查看IP地址输入 ip address 即可查看&#xff1b; 新建虚拟机的时候会遇到输入ip addr出现没有看到ip地址&#xff0c;例如如下情况时&#xff1a; 每个人上图可能会有不同显示&#xff0c;例如我的就是以eno16777…

浅谈虚拟地址转换成物理地址(值得收藏)

这里&#xff0c;我们讲解一下Linux是如何将虚拟地址转换成物理地址的 一、地址转换 在进程中&#xff0c;我们不直接对物理地址进行操作&#xff0c;CPU在运行时&#xff0c;指定的地址要经过MMU转换后才能访问到真正的物理内存。 地址转换的过程分为两部分&#xff0c;分段…

虚拟地址

虚拟地址 虚拟地址是程序运行在保护模式下&#xff0c;这样程序访问存储器所使用的逻辑地址称为虚拟地址。 那为什么需要虚拟地址呢 我们要说到寻址方式 寻址&#xff1a;根据指令内容确定操作数地址的过程&#xff0c;称为寻址 在16位的cup或者8086cpu的时候&#xff0c;他…

虚拟地址如何访问到物理地址

环境&#xff1a;32bit CPU 一、通过二级页表映射的方式访问物理地址 1、取一级页表的基地址Abase1 2、取虚拟地址的前12bit[31:20]地址O1 3、计算得到新地址Apgd(Abase1&0xFFFFF000)O1&#xff0c;此地址是PGD页表上的地址&#xff0c;取此地址中的数据Abase2 4、取虚拟地…

总线地址、物理地址、虚拟地址

1、总线地址 地址总线&#xff08;Address Bus&#xff09;是一种计算机总线&#xff0c;是CPU或有DMA能力的单元&#xff0c;用来沟通这些单元想要访问&#xff08;读取/写入&#xff09;计算机内存组件/地方的物理地址。 其实就是CPU能够访问内存的范围。 CPU寻找外部的内…

虚拟地址空间和物理地址空间

1.概念 物理地址&#xff1a;物理地址空间是实在的存在于计算机中的一个实体&#xff0c;在每一台计算机中保持唯一独立性。我们可以称它为物理内存&#xff1b;如在32位的机器上&#xff0c;物理空间的大小理论上可以达到2^32字节(4GB)&#xff0c;但如果实际装了512的内存&a…

Linux虚拟地址空间

目录 父子进程地址相同的变量值不同问题运行结果 Linux下进程虚拟地址空间分布什么是虚拟地址空间&#xff1f;进程直接访问物理内存&#xff08;无虚拟空间&#xff09;再述虚拟地址空间&#xff01;虚拟地址空间结构体是如何区域划分?解答最初的问题延伸问题: 一个pid变量怎…

虚拟地址空间

对于每一个进程都会对应一个虚拟地址空间&#xff0c;对于32位的操作系统&#xff08;其指令的位数最大为32位&#xff0c;因此地址码最多32位&#xff09;&#xff0c;虚拟地址空间的大小为B即0~4GB的虚拟地址空间&#xff0c;其中内核空间为1GB&#xff0c;如下所示&#xff…

逻辑地址、物理地址、虚拟地址

文章目录 物理地址(physical address)虚拟地址(virtual memory)逻辑地址(logical address)线性地址(linear address)或也叫虚拟地址(virtual address)地址转换 物理地址(physical address) 用于内存芯片级的单元寻址&#xff0c;与处理器和CPU连接的地址总线相对应。 虽然可以…

CPU中虚拟地址、逻辑地址(有效地址)、线性地址、物理地址

虚拟地址、逻辑地址&#xff08;有效地址&#xff09;、线性地址、物理地址 1、虚拟地址2、逻辑地址&#xff08;有效地址&#xff09;3、线性地址4、物理地址5、总结 1、虚拟地址 在实模式下&#xff0c;虚拟地址是指由程序产生的由段选择符和段内偏移地址组成的地址。经过CPU…

虚拟地址和物理地址

1、地址概念 物理地址&#xff1a;物理内存就是真实的内存&#xff0c;CPU的地址线可以直接进行寻址的内存空间大小。比如在32位平台下&#xff0c;寻址的范围是2^32也就是4G&#xff0c;并且这是固定的。在实际的应用中&#xff0c;很多的应用程序都比较大&#xff0c;计算机…

Linux操作系统~什么是虚拟地址?深度剖析进程地址空间

目录 1.所以进程的地址空间是什么呢&#xff1f; 2.mm_struct内部有什么&#xff1f; 3.虚拟地址空间与物理内存如何关联 页表 4.为什么设计这样一个进程地址空间&#xff0c;不让程序直接访问内存 Q&#xff1a;为什么子进程修改值以后&#xff0c;地址还是相同&#xf…

初识虚拟地址空间

物理地址和虚拟地址 物理寻址&#xff1a;CPU访问存储器的最原始方法就是直接用物理地址&#xff08;Physical Address, 可简称PA&#xff09;。物理地址是唯一的。 虚拟寻址&#xff1a;CPU通过生成一个虚拟的地址来访问内存&#xff0c;在访问前会把虚拟地址转化为物理地址…

虚拟地址空间,虚拟文件系统

1、虚拟地址空间 1、概念与原因 虚拟地址空间是一个抽象的概念&#xff0c;在IBM中&#xff0c;这样说道&#xff1a;它存在&#xff0c;但你看不见&#xff0c;就是虚拟的。虚拟地址空间就是这样一个东西。&#xff08;注意区分虚拟内存与虚拟地址空间&#xff09; 虚拟地址空…

彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

程序经过编译后&#xff0c;变成了可执行的文件&#xff0c;可执行文件主要包括代码和数据两部分&#xff0c;代码是只读的&#xff0c;数据则是可读可写的。 可执行文件由操作系统加载到内存中&#xff0c;交由CPU去执行&#xff0c;现在问题来了&#xff0c;CPU怎么去访问代…

使用POI导出Excel(并使用公式)

使用POI导出Excel&#xff08;并使用公式&#xff09; 使用java直接生成Excel并填充数据 可以参考POI官方文档&#xff0c;就是对Sheet&#xff0c;row&#xff0c;cell&#xff0c;Formula等操作&#xff0c; https://poi.apache.org/components/index.html 这种方式也可以生成…

poi导出excel日期格式问题

POI导出Excel的时候有时需要日期格式&#xff0c;在筛选时是这样的 private XSSFWorkbook wb null; String dateFormat "yyyy-MM-dd";//或者"yyyy/MM/dd"格式,"yyyy/M/d"这样的格式不会自动补0 public void setCell(int index,Date value,bool…

POI导出Excel详细教程

文章目录 前言一、引入jar包依赖二、创建自定义导出Excel样式类三、创建核心导出工具类四、创建导出对象实体Bean五、具体使用案例5.1.创建SQL脚本和初始化数据5.2.写一个查询所有学生信息接口5.3.查询学生基本信息返回数据格式5.4.导出Excel方法5.5.通过页面导出按钮导出Excel…