在文章之初笔者说明一下原本笔者是把整个Linux内存管理写成一篇blog的但是当笔者写到10万多字的时候发现内容太多了(目前还没有全部写完)如果不进行分篇的话就对读者太不友好了因此就分为了多个系列由于是从一篇文章分出来的因此系列各篇blog之间的关联会比较紧密最好是一篇篇的进行越多。笔者最后会把完整版的文章和分篇的都提供出来读者可以自行选取下面正式进入正文部分:1.什么是虚拟地址空间如果你了解过操作系统那么虚拟地址空间一定会是一个让你非常迷惑的点作为一个抽象的概念虚拟地址空间在操作系统中有着无与伦比的地位想要学明白操作系统管理内存的方法理解虚拟地址空间就是必须的。而虚拟地址的抽象性就意味着其必然是非常难理解的比如现在提出一个问题:现在一款大型游戏动辄就是几百G但是电脑的内存条撑死了就只有十多G十多G的内存是怎么运行起几百G的游戏的呢(后面的文章将会回答该问题) ?如果你的脑海里立刻浮现出了页表懒加载机制页框等概念并可以完整的想明白整个过程那么这篇文章并不是为你准备的(当然你也可以复习一下)如果你完全是两眼一麻黑那么这篇文章将带你初步搭建起虚拟地址空间的大框架。1.1从一个故事开始在教材的标准定义中虚拟地址空间是由一个进程的虚拟或逻辑地址所构成的地址空间一看就非常抽象说了跟没说一样现在让我们先抛弃抽象的概念从一个故事开始现在有一个千万富翁他手里面有3千万这个富翁有5个儿子富翁对他的每一个儿子都说儿啊等我死后我的三千万就都是你的了。此时他的每一个儿子都很开心他们都认为等老爹死了就可以得到他的三千万然后他们就开始幻想了大儿子想等拿到钱后就买一百双AJ二儿子想等拿到线后就去澳门当赌神.....但是此时钱还不是他们的他们就只是在做白日梦而已。富翁的儿子们平时也会向富翁要零花钱比如大儿子要500二儿子要300....但是没有儿子敢直接向富翁直接要三千万因为他们知道这是不可能的只会拿到老爹的大逼兜。可以看出这个富翁是非常鸡贼的因为他的儿子永远都不可能拿到完整的三千万(假设这个富翁是不死不灭的)他就是吊着儿子们的胃口平时最多给一点零花钱。好了故事已经讲完了现在让我们把故事中的人物替换成计算机中的概念在故事中富翁的三千万就是实际的物理内存富翁就是操作系统(只不过操作系统是个不会死的富翁)那5个儿子就是运行起来的进程儿子们幻想中的三千万就是虚拟地址空间儿子们平时申请的零花钱就是操作系统实际给他们分配的物理内存只不过操作系统更鸡贼给进程的零花钱还得要进程还回来儿子们幻想中使用三千万的方式就是进程向操作系统申请的虚拟地址空间(假内存)通过这个故事可以提取出几个结论:1.每一个进程都会有独属于自己的虚拟地址空间(幻想的三千万)。2.每一个进程的虚拟地址空间都和实际的物理地址空间一样大注理论上大小是一样的但是实际上不是不过这样比较好理解具体原因下文解释。3.操作系统管理着实际的物理空间。4.进程可以提前在虚拟地址空间中规划好要怎么使用实际的物理空间。5.进程在虚拟地址空间中规划好后操作系统不一定会马上分配实际物理空间。具体来说虚拟地址空间是一种描述描述的是进程使用物理空间的方法操作系统根据虚拟地址中的描述来给进程分配实际的物理空间是进程使用物理空间的一张说明书。那么现在的问题就是操作系统是用什么实现这个虚拟地址空间的(操作系统是怎么描述物理内存的?)不解决这个问题的话那么虚拟地址空间就永远都是抽象的概念而抽象的东西总是难以理解和记忆的必须要让虚拟地址空间依托于一个具体的实物 答案的关键就在于描述上现在再问读者一个问题在C语言中如果你要描述一个复杂的东西你会怎么做呢相信你的脑海中立刻就浮现出了结构体吧毕竟C语言描述所有复杂的东西几乎都是使用结构体的好了你已经得出了答案:虚拟地址空间就是使用结构体实现的该结构体是对实际物理地址的描述是物理空间的一张说明书。哦豁一下就变得简单了不就是结构体嘛(具体还会使用红黑树等数据结构进行组织)so easy那么下面让我们来看看这张说明书具体是什么样子的:这张图在计算机中可谓是大名鼎鼎在学习C语言的指针时一定会出现这张图但是在语言层可能会说实际物理空间就是这样划分的这么说其实是有点问题的更准确的来说这就是操作系统个每一个进程发的一张内存使用的说明书实际的物理空间是在内存条上的而内存条就是个硬件是不会标出这些花花绿绿的东西的(在内存条中的每一字节空间都是等效的)。也就是说这张说明书就是就是虚拟地址空间每一个进程都会有一张那么现在的问题就变成了要怎么使用结构体来描述出这张说明书需要把每一个字节的使用方法都描述出来吗那当然的不需要的我们从图片中可以得出虚拟地址空间的总大小是4GB(以4GB为例4GB也是最常见的)并且可以看出其中的每一个区域都有起始位置和结束位置因此实际上我们就只需要描述出起始位置和结束位置就可以了就像下面这样struct VirtualSpace { long long StackStart; //栈区起始虚拟地址 long long StackEnd; //栈区结束虚拟地址 long long HeapStart; //堆区起始虚拟地址 long long HeapEnd; //堆区结束虚拟地址 ............. };在操作系统中就是使用这样的结构体来充当虚拟地址空间(物理空间的说明书)不信那么让我们看看Linux原码中的虚拟地址空间是什么样子的:// 1. 内存描述符代表整个进程的地址空间 struct mm_struct { struct vm_area_struct *mmap; // VMA 链表头把所有内存区域串起来 struct rb_root mm_rb; // VMA 红黑树用于快速查找某个地址属于哪个区域 struct vm_area_struct *mmap_cache; // 上次查找的缓存加速访问 pgd_t *pgd; // 【关键】页全局目录指针指向页表的根硬件 MMU 靠它查表 // 记录各个段的边界让你知道代码在哪堆在哪 unsigned long start_code, end_code; // 代码段起止 unsigned long start_data, end_data; // 数据段起止 unsigned long start_brk, brk; // 堆的起止brk 指针移动就在这里 unsigned long start_stack; // 栈的起始位置 // ... 其他成员如引用计数、信号量等 };可以看到包含了很多其他东西但是确实是使用起始和结束地址来描述虚拟地址的每一个段的原码就是这样实现的(当然整套虚拟地址空间还会有很多其它复杂的东西但是主体就是对于开始位置和结束位置的描述)这下你总可以相信虚拟地址空间是使用结构体实现的吧总之虚拟地址空间绝不是一个虚无缥缈的东西它是依托于具体的技术实现的(把抽象概念依托于具体实物是非常重要的)。我们已经知道了虚拟地址空间是使用结构体实现的那么下面就有了另一个问题操作系统要怎么把进程在虚拟地址空间中申请的空间映射到实际的物理内存空间中看到映射这两个字相信读者已经有一些猜想了:嗯....映射应该要使用哈希表吧没错聪明的你又猜出了答案操作系统就是通过哈希表来完成虚拟地址空间到物理空间的转换的这张哈希表在操作系统中叫页表简单来说就是这样的:进程先在虚拟地址空间中申请虚拟地址然后再把虚拟地址填入页表进程在页表的左侧填虚拟地址操作系统在右侧填物理地址经由页表的映射后进程申请到的虚拟地址就变为了物理地址然后就可以使用物理地址在物理空间中存储数据了。此处出现了两个新名词:虚拟地址和物理地址先来解释一下。1.2虚拟地址与物理地址虚拟地址就是在虚拟地址空间中申请的地址该地址是根据结构体中的描述进行申请的比如如果在描述中栈区的起始地址是10结束地址是80申请了50号地址那么该地址就位于栈区了在实际情况中虚拟地址的大小和计算机的位数有关32位系统下为4字节64位系统下为8字节熟悉吧和C语言中指针的规定一模一样没错我们在语言层面申请的指针变量存储的都是虚拟地址根本就不是物理地址也就是说虚拟地址空间的大小完全由计算机的位数决定比如32位系统能够产生2^32个虚拟地址那么虚拟地址空间的大小就是4GB64位系统能够产生2^64个虚拟地址虚拟地址空间的大小就是16EB(内存条硬件目前不可能做到这么大)。然后来看看物理地址物理地址就是实际物理空间中(内存条)的地址物理空间中的每一个字节都会有一个对应的物理地址物理地址也和计算机的位数有关理论上物理空间的最大值是取决于计算机的位数的比如32位系统能够产生2^32个有效地址那么内存条最大就是4GB而64位系统能够产生2^64个有效地址那么内存条最大就是16EB但是受限于硬件的技术目前4GB的内存条是造的出来的因此在32位系统中虚拟地址空间和物理地址空间可以是一样大的(只要使用了4GB大小的内存条)但是目前16EB的内存条是造不出来的因此在64位系统的电脑中虚拟地址空间是一定会比物理空间大的也就是说在计算机中虚拟地址空间的大小 物理空间。要注意的是就算是32位的电脑其虚拟地址空间也是有可能比物理空间大的因为此时虚拟地址空间的大小一定是4GB但是实际使用的内存条可能只有3GB那么虚拟地址空间就会比物理地址空间大了。提醒你一下只有物理空间(内存条)中能够存储数据虚拟地址空间是假的是操作系统给进程发的物理空间的一张说明书一定要搞明白虚拟地址空间是不可能存放实际数据的从生活的角度看虚拟地址空间的大小 物理空间的情况就是描述与实物不符这在生活中是非常常见的(薯片的包装袋中大部分都是空气只有少量的薯片) 在后续系列的文章中为了便于讲解实现虚拟地址空间的具体技术细节本人将以32位系统的电脑为例并假设该电脑中使用的是4GB的内存条(这在32位系统的电脑中是很常见的)在这种情况下虚拟地址空间和物理空间的大小就是相同的这对于我们理解虚拟地址空间的具体技术实现细节是非常有帮助的。最后再来看看页表页表在图中就是一个简单的哈希表左侧填入的是进程申请的虚拟地址右侧可以映射到实际的物理地址非常好理解当然页表实际的映射非常复杂不过可以先这么理解(原理就是这样的但是技术实现细节非常复杂)本人在后续文章再细说最后要说明的是每一个进程都会有独属于自己的虚拟地址空间和页表也就是说不同进程之间的虚拟地址空间和页表各自独立。2.为什么要有虚拟地址空间看到这里相信读者对虚拟地址空间已经有了一个大概的理解了至少它在你的脑海中不再仅仅只有虚拟地址空间这个抽象的名词了它变为了一个具体的结构体和哈希表那么现在相信你一定非常好奇:为什么要有虚拟地址空间呢直接把数据存储在内存条中它不香吗相信你也感觉出来了虚拟地址空间就像是进程和物理空间中的一个中间商在实际生活中中间商可不是一个好名词但是在计算机的世界中可不同(实际上在生活中中间商也是不可或缺的)在计算机的世界中有一句名言计算机中的所有问题都可以使用添加一个中间层的方式来解决。这是真的添加中间层的操作在无论是在计算机的硬件问题中还是在计算机的软件问题中都是一个非常好的解决方案。回到正题现在的事实就是虚拟地址空间在计算机中已经广泛存在了这就说明没有虚拟地址空间一定会出现某种问题而想要彻底的搞明白为什么要有虚拟地址空间就必须要看看没有虚拟地址空间会出现什么问题有虚拟地址空间又会起到什么效果下面就来看看吧。虚拟地址空间可以起到保护内存有效数据的作用。为了了解具体的原因让我们先回到富翁的那个故事中富翁把他的三千万存在银行卡中他的4个小儿子都不知道银行卡的密码因此他们在向富翁要钱时必须先征求富翁的同意但是富翁的大儿子使用了某种特殊的方法知道了富翁的银行卡密码那么从此以后这个大儿子要钱就不需要征求富翁的同意了他想拿多少就拿多少就算事后富翁发现了这种情况被大儿子用出去的钱也已经回不来了。换成计算机中的概念银行卡密码就是虚拟地址空间如果没有虚拟地址空间的话那么进程就可以直接访问到内存这是一个非常危险的行为此时指针就可以直接申请物理地址并随意的指向内存中的任意一个位置并对该位置进行读写操作 要知道操作系统也是在内存中的如果有个不长眼的指针指向了内存中的存储操作系统的位置并且还更改了数据那么你的操作系统很可能会挂掉了。但是有了虚拟地址空间后指针就只能申请到虚拟地址实际的物理地址只能由操作系统进行分配(相当于申请物理地址时必须要征求操作系统的同意)当操作系统发现进程使用了不正确的指针操作时(比如野指针)就会直接把进程杀掉了。也就是说在有了虚拟地址空间后进程在使用物理空间时操作系统会先检测一次如果发现不长眼的进程要更改内存中的有效数据那么就会直接杀掉该进程这就起到了保护了内存中有效数据了的效果了。提出一个问题:没有虚拟地址空间时进程直接使用物理空间的话操作系统就不能进行检测吗答案是能但是会非常困难而且此时的检测多半就是事后检测事后检测的意思就是当进程已经改变物理空间中的数据后操作系统才可以检测出来(因为在没有改变前是不会有任何征兆的)但是被改变的数据已经回不来了就算操作系统把这个该死的进程杀掉也找不回内存中原本的东西了但是在有了虚拟地址空间后进程改变物理内存的操作就是可以预见的了而且还可以在页表中设置读写权限来提前预防进程的错误操作。就像是警察抓杀人犯一样一种是在杀人犯杀了人后警察才逮捕了他但是此时死去的人已经不会回来了另一种是警察提前分析作案动机在杀人犯还没有杀人前就把杀人犯逮捕了那么杀人犯就不会造成任何实际的影响了。就单单凭保护内存数据这一点虚拟地址空间就已经是必须的了毕竟内存中的数据实在是太重要了。虚拟地址空间可以增强进程之间的独立性。要知道每一个进程都是有独属于自己的虚拟地址空间的并且每一个进程的虚拟地址空间都是一样的(都是那个结构体)那么就出现了一个问题不同的进程会申请到同一个虚拟地址吗答案是会而且这种情况是经常发生的但是这会造成什么影响吗富翁的大儿子幻想用三千万买1000双AJ,富翁的三儿子同样也幻想用三千万买1000双AJ幻想相同会造成什么实际影响吗那当然是不会的因为实际分配物理地址的是操作系统比如进程A申请了5号虚拟地址进程B也申请了5号虚拟地址但是操作系统在将虚拟地址映射到实际的物理地址时(填页表)可以将进程A的5号虚拟地址映射到8号物理地址将进程B的虚拟地址映射到10号物理地址如下图也就是说有了虚拟地址空间后进程之间出现物理空间冲突的情况几乎是不可能发生的了并且又由于虚拟地址空间保护内存数据的效果一个进程也不可能使用指针来改变另一个进程的数据了(操作系统直接提前杀掉)这就起到了增强进程间独立性的效果了。(注操作系统杀掉进程的表现就是程序崩溃虚拟地址空间可以减少内存碎片提高操作系统管理内存的效率。先来解释一下什么是内存碎片内存碎片分为内碎片和外碎片外碎片就是在申请一块连续的大内存空间时分开申请了多次如果分开的多次申请是间断的又或者归还的内存是不连续的那么这个连续的内存空间就被切开了此时产生的内存碎片就是外碎片即由于可用内存不连续而产生的内存碎片如下图下面再来看看内碎片所谓的内碎片就是申请内存空间的人申请了一大块内存但是这个人就使用了一小部分那么没有使用的那一部分就是内碎片了。这种情况是经常发生的因为在申请内存时会有很多的对齐规则(了解结构体对齐规则的话应该会知道这一点)也就是说可能会出现你只向操作系统申请了5字节的内存但是由于对齐规则操作系统给你分配了8字节内存那么有3字节内存就浪费了此时这浪费的3字节内存就是内碎片。最后总结一下内碎片和外碎片最明显的区别就是外碎片是在操作系统管理的内存中产生的(在内存池中也会产生)是可以继续分配给其它用户的而内碎片是在用户申请到的内存中产生的不能分配给其它用户也就是说内碎片无法重复利用但是外碎片还是可以继续使用的只不过比较难用最后要说明的是一般来说内碎片都是比较小的但是外碎片不一定外碎片强调的主要是可用内存不连续问题和大小没什么关系。如果在物理内存空间中出现了大量的外碎片的话那么此时如果进程直接向物理内存空间申请连续大块的内存的话操作系统就会非常为难比如进程向操作系统申请连续的50MB物理内存此时物理内存中是有这么多内存的但是内存是碎片化的东边有一块20MB大小的空间西边有一块30MB大小的空间那操作系统要怎么办呢答案就在于虚拟地址空间。在了解了内碎片和外碎片的概念后首先要对结论进行一次修正即虚拟地址空间可以减少内存碎片提高操作系统管理内存的效率准确来说是: 虚拟地址空间可以提高对于物理内存中外碎片使用率以此达到减少内存碎片的效果。下面来解释原因。首先如果没有虚拟地址空间的话那么进程就是直接申请物理内存空间并使用物理地址的此时如果进程要申请100MB的空间物理内存中有但是都是碎片化的那么此时操作系统也没有办法它就只能把进程申请的100MB内存给切开分散到各个外碎片中(因为操作系统不能覆盖掉内存中已存在的有效数据)此时进程是会发现自己申请的连续内存实际上不是连续的了放在编程中的体现就是你申请了一个100MB大小的数组你应该是能够使用连续的下标访问到数组中的任意一个元素的但是由于此时你的数组在内存中是分散存储的那么此时数组的下标就不是连续的了(因为下标本质上就是地址是数组首元素地址指定个元素的偏移个数)可能会出现[50]可以访问到数组中的第51个元素(首元素的下标是0)但是要访问第52个元素时下标就变为了[180]这可太恐怖了这就相当于把数组给禁掉了毕竟你无法确定在物理内存有没有足够大的连续空间就算在你的电脑上有你也无法确定当你的程序跑在其他人的电脑上时会不会出现问题这可真是太痛苦了想想就令人害怕毕竟编程界是绝对不能失去数组的并且上方说明的还只是其中一个问题更加具体的来说所有连续申请的内存都可能会出问题在这种情况下大型软件就是不可能诞生的(比如原神王者荣耀CS等)。下面来看看虚拟地址空间是怎么解决这个难题的首先明确一点在物理内存空间中外碎片是不可避免的上方问题产生的关键就在于进程能够感知到自己申请的连续内存被切开了既然外碎片不可避免而为了避免内存浪费操作系统又一定会给进程分配外碎片那么进程申请的连续空间就总是有可能会被切开的因此解决问题的关键不在于解决外碎片而在于:让进程无法感知到自己申请的连续内存被切开了也就是说就算进程申请的内存在物理空间中是分散的也不能让进程发现必须让进程认为自己申请的内存是连续的而虚拟地址空间加页表的效果就完美的达成了这一点如下图可以看到进程申请的内存在虚拟地址空间中是连续的但是经过了页表的映射后在物理地址空间中就不连续了此时进程不会发现自己申请的连续空间实际上被操作系统给切开了进程只认虚拟地址这也就是为什么在语言中使用的地址都是虚拟地址了只有使用虚拟地址才能够实现给进程分配大块连续的内存(至少在进程的视角中是如此)才能够让数组下ban位有人可能会问了这个进程申请的内存在实际物理空间中不还是间断的吗不会有什么影响吗答案是有但是微乎其微只需要操作系统进行一次简单的哈希映射就行了如果还是不理解的话那么我们让情景再具体一点再极端一点假设进程申请了连续的两字节内存但是在物理内存中只存在多个1字节的外碎片如下图现在就很好理解了进程就只向操作系统申请了2字节连续内存但是在物理内存中没有连续的2字节内存了只有多个1字节的碎片因此在实际物理内存中操作系统就只能把进程申请的连续内存给切开但是在经过了页表映射后被切开的物理内存在进程的虚拟地址空间中就变的连续了而进程只认虚拟地址空间因此在进程看了它就是申请到了连续的空间表现在编程中就是我们申请了一个char arr[2]尽管这个数组中的两字节的实际的物理内存中是分散的但是我们依旧可以使用arr[0]和arr[1]这种连续的地址来访问该数组中的元素这也进一步说明了在语言层使用的地址都是虚拟地址和物理地址没什么联系。而对于操作系统来说要把连续的虚拟地址和分散的物理地址之间进行转换是非常容易的几乎是不需要消耗额外时间的那么虚拟地址空间就完美的解决了连续内存问题了。最后再来说明一下为什么虚拟地址空间提高了内存外碎片的使用率如果你已经理解了上面的知识那么这个问题应该是可以比较轻松的回答出来的不过本人还是在此处做一些说明吧我们假设在物理内存空间中存在两块外碎片一块是10kb一块是5kb此时应该进程要申请15kb的内存这个进程要求申请到的内存必须是连续的否则进程就无法正常运行。那么如果没有虚拟地址空间的话操作系统就不能给该进程分配空间碎片就无法被使用此时该进程就只能被操作系统挂起等待等到物理内存空间中有连续的15kb内存时再把空间分配给该进程但是如果有了虚拟地址空间的话该进程看到的就只有虚拟地址空间也就是说它只要求自己申请到的内存在虚拟地址空间中是连续的那么操作系统就可以直接把物理内存中分散的内存碎片直接分配给该进程只需要进行一次页表的映射就可以了。从上方的例子就可以明显看出在没有虚拟地址空间时那两块内存碎片就只能一直被闲置着但是有了虚拟地址空间后那两块内存碎片就可以立刻被使用从这个角度也可以看出虚拟地址空间提高了进程的运行效率(因为进程可以直接获取到自己需要的连续内存不需要被挂起等待了)所以虚拟地址空间就可以提高内存外碎片的使用率了。提高内存外碎片的使用率还会带了另一个效果那就是提高内存的命中率简单来说就是CPU执行程序是分批次执行的比如一个进程的代码占用了10kb内存而CPU每次就只能执行1kb的代码那么在理想情况下该进程要被CPU执行10次操作系统每次喂给CPU该进程的1kb代码进行执行此时内存的命中率就是100%即操作系统喂给CPU的每一字节都是该进程的有效数据存储空间(此处的命中率更加偏向软件层面仅仅只是为了便于理解实际上的命中率是一个偏向硬件的概念)但是一般来说这是不可能的就算是有了虚拟地址空间技术也不可能让内存的命中率达到100%。我们先来看看没有虚拟地址空间时的情况我们取最坏的情况进行假设(在编程中假设一般都是要取最坏情况的)即该进程的每1字节数据在物理内存中都是分散的也就是说在物理内存的1kb中只有1字节是该进程的有效数据有点极端了命中率低的离谱此时由于没有虚拟地址空间进行参照操作系统就只能喂给CPU连续的1kb物理内存而在这连续的1kb物理内存中只有1字节是进程的有效数据也就是说CPU要分批次执行几千次才能执行完这个该死的进程情况比较极端但是这明确的体现出:在没有虚拟地址空间时如果内存碎片增多的话确实是会导致内存命中率快速下降的在这里要补充一点内存外碎片是会被越切越小的因此命中率就会越来越低。而在有了虚拟地址空间后首先它减少了内存外碎片的产生这就已经提高了内存的命中率了其次更加重要的是当代CPU在执行程序时看的也是虚拟地址(逻辑地址)也就是说此时的命中率就更像是变成了虚拟地址空间的命中率了具体的技术细节是在CPU中存在一个MMU设备该设备可以根据页表进行虚拟地址到物理地址的转换(实际上虚到实的转换工作都是由MMU独立完成的操作系统不会参与)具体技术细节在下一章在进行说明。既然命中率变为了虚拟地址空间的命中率那么命中率就会变得非常高了毕竟进程在自己的虚拟地址空间中一定是连续的也就是说给CPU的每一字节的虚拟地址标识的都是内存的有效数据不过就算如此命中率依旧还是达不到100%的具体原因和进程的调度缺页中断等知识有关这里不多解释。如果觉得难理解的话可以先不管什么MMU可以认为有了虚拟地址空间后操作系统就可以通过查看页表的方式提前得知进程使用物理内存空间的情况那么操作系统就可以根据这张说明书直接喂给CPU物理内存空间中分散的内存比如在上方那个极端的例子中操作系统就可以不喂给CPU连续的1kb物理内存而是通过查看进程页表的方式把进程分散开了的有效数据先进行集中再喂给CPU那么自然就可以提高内存命中率了(先这么理解后面再进行修正)。3.小结看到这里相信读者已经对虚拟地址空间存在的必要性有了比较深刻的理解了在这里本人要说明的是在我看来学习一个具体的技术前是绝对不能上来就直接看技术细节的首先必须先对该技术有一个大体的了解也就是宏观认识然后更重要的是你得知道为什么要有这个技术即这个技术可以解决什么实际问题这是非常重要的本人在学习虚拟地址空间时查看了大量书籍都没有明确的写出虚拟地址空间有什么用具体可以解决什么问题就算写了也是非常的模糊因此在学技术细节时是比较痛苦的因为我并不知道这东西有什么用。因此本人在此处花费了大量的篇幅来说明虚拟地址空间的必要性和实用性希望能够让读者在后续文章学习虚拟地址空间的具体技术细节时不会那么痛苦。