linux虚拟内存原理

191 阅读4分钟

一、程序加载过程

我们程序是从磁盘中编码的,代码执行的时候,首先加载至内存中,然后到寄存器中(寄存器是存放各种给cpu计算用的地址、数据用的),最后至CPU;


二、内存映射

Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的,进程就可以很方便地访问内存,也就是我们常说的虚拟内存虚拟内存。

根据地址范围的不同,我们分为32位和64位,一般我们见的比较多的就是32位操作系统,这里就都介绍一下;

虚拟内存被分为用户空间和内核空间两部分;这两个其实在权限上就有一定的区分了;当我们的进程在用户态的时候,只能访问用户空间只有进入内核态,才能访问内核空间;

我们看一下32位地址和64位地址空间的分布:

这里会有一种错觉,我们每个进程都占有了这么多的空间,那么多个进程怎么办?事实上并没有那么多的空间,其实这个本质是就是一种“自欺欺人”的做法,每个进程都以为自己占据了全部的地址空间;其实只有在实际使用虚拟内存的时候,才会分配物理内存;

用户空间是每个进程私有的;内核空间是每个进程共享的,不与任何用户进程共享;

通过内存映射将虚拟内存地址映射到物理内存地址,对实际使用虚拟内存并分配的物理内存进行管理;

作用就是:只会将某一进程此刻需要的内存大小映射到物理内存,其它暂时不需要的内容交换到硬盘存储即可。当进程需要使用在硬盘中的内容或者需要动态申请内存时,操作系统会利用缺页操作,触发一次内存映射,将另外的物理内存映射进虚拟内存,供程序使用,这样对于进程而言,则认为内存总是够用的。
各个进程均拥有3G虚拟内存,那么操作系统是如何做到各进程所使用的实际物理内存不会互相占用呢?实际上,各个进程均有自己的内存映射表。任意一个时刻,在一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个4GB的虚拟地址空间,这个虚拟地址空间是面向此进程的。当进程发生切换的时候,虚拟地址空间也随着切换。由此可以看出,每个进程都有自己的虚拟地址空间,只有此进程运行的时候,其虚拟地址空间才被运行它的CPU所知。在其它时刻,其虚拟地址空间对于CPU来说,是不可知的。所以尽管每个进程都可以有4 GB的虚拟地址空间,但在CPU眼中,只有一个虚拟地址空间存在。虚拟地址空间的变化,随着进程切换而变化。\


内存映射原理

虚拟内存映射到物理内存地址,内核为每一个进程维护了一张表,记录了他们对应的映射关系;

而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

Linux采用了四级页表来管理内存页,多级页表就是把内存分成区块来管理,将原来的映射关系改成区块索引和区块内的偏移。由于虚拟内存空间通常只用了很少一部分,那么,多级页表就只保存这些使用中的区块,这样就可以大大地减少页表的项数。


三、虚拟内存空间分布

虚拟地址空间0~3G用于用户层 虚拟地址空间3~4G用于内核层

这个图可能是我们见过最到的了

地址空间保存的数据:

栈:保存局部变量、函数形参、自动变量。数据具有先进先出、后进后出的特 点;

堆:保存由malloc、ralloc、calloc分配空间的变量。

BSS段:保存未初始化或初始化为0的全局变量和静态局部变量。

data段(数据段):保存初始化不为0的全局变量或者static修饰的变量。

代码段:保存代码、可执行代码、字符串字面值、只读变量