Linux中的虚拟地址、物理地址和内存管理方式(一)
一、简单介绍下早期的内存实现:(可略过)
1、在早期的计算机中,运行一个程序的特点是:
(1)会把这些程序全都装入内存,
(2)程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址。
2、在早起的内存实现方式中出现的问题:
(1)当计算 机同时运行多个程序时,必须保证这些程序用到的内存总量要小于计算机实际物理内存的大小。
(2)进程地址空间不隔离。进程间可以相互修改数据
(3)内存使用率低。运行一个进程,必须在内存中为它分配实际进程大小的空间(不管当前这些空间是否都会用到)
(4)程序运行的地址不确定。分配内存时,只是单纯的从内存空间找一个足够满足进程要求的空间,所以进程运行的地址具有随机性。
二、虚拟地址技术
为了解决上面的问题,一些大牛们就提出了传说中的虚地址技术。即:在用户进程和实际的物理内存之间加一个中间层(虚拟地址),让用户进程只能访问虚拟地址,并且把虚拟地址物理地址转换的实现交给操作系统。
采用虚拟地址的特点:
1、当创建进程时,
(1)OS会为每个进程分配一个4G大小的虚拟地址空间,且每个进程都有3G的用户空间和1G的内核空间。但最后这1G的内核空间中的内容对于不同进程来说是一样的(之所以是4G大小,是因为在32位操作系统中,一个指针的大小是4btyes,所以能访问的地址空间就是0x00000000~0xFFFFFFFF == 4G)。
(2)这4G的虚拟地址空间是操作系统虚拟出来的,并不是真实存在的。
(3)每个进程只能访问自己的虚拟地址,无法访问别的进程的虚拟地址。
(4)虽然每个进程的虚拟地址都有4G大小,但并不是这4G的地址都可以由用户任意使用。在Linux中,系统把虚拟地址划分成2个部分(如下图所示):
注:1、任意一个时刻,在一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个4GB的虚拟地址空间,这个虚拟地址空间是面向此进程的。
当进程发生切换的时候,虚拟地址空间也随着切换。每个进程只有在运行的时候,其虚拟地址空间才被运行它的CPU所知。在其它时刻,其虚拟地址空间对于CPU来说,是不可知的。所以尽管每个进程都可以有4 GB的虚拟地址空间,但在CPU眼中,只有一个虚拟地址空间存在。虚拟地址空间的变化,随着进程切换而变化。
2、内核空间与物理内存空间的映射和用户空间与物理内存空间的映射方式是不同:
虽然内核空间占据了每个虚拟空间中的最高1GB字节,但映射到物理内存却总是从最低地址(0x00000000)开始的,之所以这么规定,是为了在内核空间与物理内存之间建立简单的线性映射关系。其中,3GB(0xC0000000)就是物理地址与虚拟地址之间的位移量,在Linux代码中就叫做PAGE_OFFSET。
我们来看一下在include/asm/i386/page.h头文件中对内核空间中地址映射的说明及定义:
#define __PAGE_OFFSET (0xC0000000)
……
#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
对于内核空间而言,给定一个虚地址x,其物理地址为“x- PAGE_OFFSET”,给定一个物理地址x,其虚地址为“x+ PAGE_OFFSET”。
这里再次说明,宏__pa()仅仅把一个内核空间的虚地址映射到物理地址,
这种方法决不适用于用户空间,用户空间的地址映射要复杂得多,它通过分页机制完成。
(图一)4G虚拟地址空间的整体结构
(图二)内核空间分配图
(图三,地址分布图)
2、与虚拟地址相对的就是我们内存条实际的大小了。以本人PC为例:我用的是2G内存,那么实际的物理地址空间就是:0x00000000~0x1FFFFFFF
三、分段和分页
1、采用虚拟地址技术后出现的新问题是:虚拟地址和物理地址之间的映射方式。大牛们提出的解决方式就是分段、分页和段页式了。具体实现我们用一个例子来演示:
eg:假设有两个进程 A 和 B ,进程 A 所需内存大小为 10M ,其虚拟地址空间分布在 0x00000000 到 0x00A00000 ,进程 B 所需内存为 100M ,其虚拟地址空间分布为 0x00000000 到 0x06400000 。
(1)采用分段的方式
这种分段的映射方法虽然解决了上述中的问题(2)和问题(4),但并没能解决问题(1)(3),即内存的使用效率问题。在分段的映射方法中,每次换入换出内存的都是整个程序, 这样会造成大量的磁盘访问操作,导致效率低下。所以这种映射方法还是稍显粗糙,粒度比较大
(2)采用分页式
分页的基本方法是,将地址空间分成许多的页。每页的大小由 CPU 决定,然后由操作系统选择页的大小。目前 Inter 系列的 CPU 支持 4KB 或 4MB 的页大小,而 PC 上目前都选择使用 4KB 。按这种选择, 4GB 虚拟地址空间共可以分成 1048576 个页, 512M 的物理内存可以分为 131072 个页。
分页的思想是程序运行时用到哪页就为哪页分配内存,没用到的页暂时保留在 硬盘上。
四、这里对分段、分页只做了一个简单笼统的介绍,如果想更深入的理解底层的实现机制,请看下一篇
Linux中的虚拟地址、物理地址和内存管理方式(二)