页映射与可执行文件的装载

Posted vector6_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了页映射与可执行文件的装载相关的知识,希望对你有一定的参考价值。

页映射与可执行文件的装载

我们知道可执行文件只有装载到内存之后才能被CPU处理执行,早期的程序装载十分简陋,装载的基本过程就是把程序从外部存储器中读取到内存中的某个位置。随着现代虚拟地址空间及MMU的发展,在多进程、多用户、虚拟存储的操作系统下,可执行文件的装载变得更复杂了。

进程的虚拟地址空间

每个程序被运行起来后,它将拥有自己独立的虚拟地址空间。这个虚拟地址空间的大小由计算机硬件平台决定,CPU的寻址范围决定了地址空间的理论上限。32位硬件平台决定了虚拟空间的地址为0到2^32-1,即4G虚拟空间大小。以此类推,64位硬件平台具有64位寻址能力,它的虚拟地址空间理论可达到17179869784GB。

从程序的角度看,我们可以通过判断C语言程序中的指针所占的空间来计算虚拟地址空间的大小。一般情况下C语言指针的大小与虚拟空间的位数相同,如32位平台下的指针为32位(4字节),64位平台下的指针为64位(8字节)。

32位平台下的4G虚拟空间我们能否完全任意使用?

我们知道虚拟空间都在操作系统的掌控之中,进程只能使用那些操作系统分配给进程的地址,如果访问未经允许的空间,可能对其他进程产生影响,那么操作系统就会捕获这些访问, 将进程的这种访问当作非法操作,强制结束进程。即linux 下的 “segmentation fault”。

linux系统中,将进程的虚拟地址空间分为两部分,操作系统占1G,用户空间占3G。即原则上讲,我们的进程最多可以使用3G的虚拟空间,也就是说整个进程在执行的时候,所有的代码、数据包括通过C语言malloc等方法申请的虚拟空间之和最多为3G。

装载的方式

上面说了早期的程序装载的基本过程就是把程序运行所需的指令和数据从外部存储器中全部装入到内存中,这种简单的装入方法称为静态装入。

而现代研究发现,程序运行时是有局部性原理的,所以我们可以将程序最常用的部分驻留在内存中,将一些不常用的数据存放在磁盘中,这即是动态装入。

动态装载典型的方法有覆盖装入和页映射。

覆盖装入

覆盖装入的基本原理是程序员在编写程序时必须手工将程序分割成若干块,然后编写辅助代码来管理这些模块何时应该驻留内存而何时应该被替换掉。(即覆盖管理器)

在有多个模块的情况下,程序员需要手工将模块按照它们之间的调用依赖关系组织成树状结构。覆盖管理器对多模块的管理需要注意:1)树状结构中从任何一个模块到树的根(即main)模块都称为调用路径,当该模块被调用时,整个调用路径上的模块必须都在内存中。2)禁止跨树间调用。任意一个模块不允许跨过树状结构进行调用。

由于跨模块间的调用都需要经过覆盖管理器,以确保所有被调用到的模块都能够正确地驻留在内存,而且一旦模块没有在内存中,还需要从磁盘或其他存储器读取相应的模块,所以覆盖装入的速度肯定比较慢。覆盖装入是一种折中的方案,是典型的利用时间换取空间的做法。

覆盖装入在发明虚拟存储之前使用比较广泛,现在已经基本被淘汰了。

页映射

页映射属于虚拟存储机制的一部分。相似地,动态装载过程的页映射也是利用了局部性原理,不会一下子就把程序的所有数据和指令都装入内存,而是将内存和所有磁盘中的数据和指令按照”页“(Page)为单位划分,按页来操作和装载。

假设程序为32KB,那么程序总共被分为8个页。如上图所示,16KB的内存无法将32KB的程序同时装入。如果程序刚开始执行时的入口地址在P0,这时装载管理器发现程序P0不在内存中,于是将内存F0分配给P0,并且将P0的内容装入F0;运行一段时间后,程序需要用到P5,于是装载管理器将P5装入F1;同样的,当使用到P3和P6时,它们被分别装入到F2和F3,映射管理即如上图所示。

此时,内存已经被装满,但是若这时程序需要访问P4,那么装载管理器就必须根据相关的策略来放弃目前正在使用的4个内存页中的一个来装载P4,即页面置换算法如 FIFO、LUR等。

当然上述的装载管理器即是操作系统的存储管理器。目前主流操作系统都是按照这种方式装载可执行文件的。

从操作系统的角度看程序装载

进程的创建

事实上,从操作系统的角度来看,一个进程最关键的特征是它拥有独立的虚拟地址空间。很多时候一个程序被执行同时都伴随着一个新的进程的创建,进程的创建流程主要为:

1.创建一个独立的虚拟地址空间

我们知道一个虚拟空间由一组页映射函数将虚拟空间的各个页映射至相应的物理空间,创建一个虚拟空间实际上并不是创建空间,而是创建映射函数所需要的相应的数据结构。

2.读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系。

上面第一步的页映射关系函数是虚拟空间到物理内存的映射管理,这一步所建立的是虚拟空间与可执行文件的映射关系。

我们知道,当程序执行发生页错误时,操作系统将从物理内存中分配一个物理页,然后将该“缺页”从磁盘中读取到内存中,再设置缺页的虚拟页和物理页的映射关系,这样程序才能正常运行。但是当操作系统捕获到缺页错误时,它应该知道程序当前所需要的页在可执行文件中的位置。即可执行文件与虚拟空间的映射关系。

3.将CPU的指令寄存器设置成可执行文件的入口地址,启动运行。

以上是关于页映射与可执行文件的装载的主要内容,如果未能解决你的问题,请参考以下文章

《程序员自我修养》阅读笔记-可执行文件装载与进程

读书笔记|《程序员的自我修养》- 04 可执行文件的装载与进程

读书笔记|《程序员的自我修养》- 04 可执行文件的装载与进程

读书笔记|《程序员的自我修养》- 04 可执行文件的装载与进程

装载与动态链接

虚拟内存与进程地址空间