高端内存与低端内存漫谈

Linux 内核知识点讨论区.内存管理.  

低端内存与高端内存,在32位CPU中才会出现的概念,这是因为32位系统中,内核的地址空间是3G-4G(也可以配置成2G-4G,但不耽误理解高端内存与低端内存的概念)。3G-4G,整个内核的虚拟地址空间就1G。1G的虚拟地址,怎么能够管理到实际2G、3G甚至是4G的物理地址呢?自然就想到了一种方法叫:从一一映射中拿出一部分的地址空间作动态的映射

首先我们必须有常识:一一映射是天生的,是土著!所有的事物都有其发展史,很早以前没有虚拟地址物理地址的区别,都是物理地址。因为彼时的计算机很简单,没有存储,没有网络,就一段程序放内存,然后在CPU中执行,这是计算机的蛮荒时代。后来,随着人类需求的日新月异,我需要在一台计算机上跑若干个程序,需要多线程,需要调度,于是MMU、页表就设计出来了,辅助多线程的实现!页表简直就是第一只猿猴站起来的那根拐杖,意义非凡,它内存的使用更自由了:进程A通过页表能找到自己的物理内存,进程B通过自己的页表能找到自己的物理内存,同理,进程C,D,E....,页表机制就是一个虚拟层,让不同的进程共享内存,这不就是虚拟机的概念嘛^V^。

页表机制是怎么提出的?来,跟着哥的思路走。蛮荒时代,CPU有32位的寻址空间(实际可能是8位),那个简单的单进程中出现了一个访存地址0xf6,告诉CPU从0xf6中取出数据计算,于是CPU乖乖地从0xf6地址取数,这就是蛮荒时代访存做法咯。这个函数就是 y = x 嘛,程序中的访存地址就是虚拟地址x,实际读取的物理地址就是y了。后来,进入多线程时代之后,进程中出现了一个访存地址0xf6,告诉CPU从0xf6中取出数据,于是CPU不乖乖地从物理地址0xf6取数,而是参考了页表,从F(0xf6)去去了,也就是说现在函数变成了y = f(x)。x还是虚拟地址,y是实际物理地址。

我说这一堆,其实就是为了说明一件事情,一一映射,即y=x,才是计算机页表映射的土著,所以内核中前部分是一一映射就不足为奇了,你不需要去纠结为什么是一一映射,无他,这里记录着一部计算机发展史。

回到最初的问题,内核虚拟地址总共1G,如果全部采用一一映射,这1G的空间最多能cover住1G的内存,超过1G的部分内核根本就掌控不了,这是不能接受的。解决方法是从一一映射中拿出一部分作动态的映射。什么意思?从此,我整块内存的前896M(低端内存)被一一映射到了内核地址空间3G---3G+896M的部分,896M之后(高端内存)现用现map

这里就是理解高低端内存最重要的一道坎了,如果能坚持到这里,说明你对该部分已经比较懂了!继续!

下面是我对该部分的几个疑问以及解答

疑惑1)目前看内核空间被分成了两部分,一部分是直接映射区域区域3G--3G+896M,另一部分是3G+896M以上的临时映射区域。内核这1G都映射成1G似乎也没啥问题,因为page数组(管理所有物理内存)位于低端内存,所以全部的内存已经被内核cover住了,并且我内核实际可使用的虚拟地址并没有变化,那么内核就全部用低1G,高于1G的都给用户态使用不就得了?

解答1)为了解决一个非常重要的事情:内存的碎片化。系统运行过程中,很可能出现的一种情况是内核的1G空间被total碎掉,比如第3G,3G+8k,3G+16k....的空间被使用,而3G+4k,3G+12k......的空间是闲着的,也就是说整个内核虚拟地址空间有512M可以用,但是你分配一个8k的地址分配不到!!这里就是所谓的kmalloc和vmalloc的区别咯,预留这样一个地址空间,你就可以使用vmalloc分配线性地址连续而物理地址可以不连续的地址了,也算是在碎片化严重的内存中见缝插针地成功分配了一块区域。

原文地址:https://www.cnblogs.com/honpey/p/6619140.html