目录
一、进程虚拟地址空间
?二、装载方式
2.1 覆盖装入(Overlay)
?2.2 页映射
?三、从操作系统角度看可执行文件的装载
3.1 进程的建立
3.2 页错误?
四、进程虚拟空间分布?
4.1 ELF文件链接视图和执行视图
4.2 堆和栈
4.3 堆的最大申请数量
4.4 段地址对齐?
4.5 进程栈初始化?
五、Linux内核装载ELF过程的简介
?六、Windows的PE的装载
小结?
可执行文件只有装载到内存中才能被CPU利用。随着MMU( Memory Management Unit )(内存管控单元)的发展、多进程、多用户、虚拟存储的操作系统出现后,其装载变得复杂起来。
一、进程虚拟地址空间
程序和进程有什么区分:
程序可以看作代码。进程为执行整个代码的过程。

硬件位数决定了地址空间的上线,如32位的计算机。地址空间为0x0000000-0xFFFFFFFF,为4GB。64位的地址空间为:0x0000000000000000-0xFFFFFFFFFFFFFFFF,为8GB。
同样C语言指针的位数与虚拟地址位数相同。32位的指针位数同样为32位。
内存如何分配的呢?在Linux系统下内存分为两部分,一部分为系统内存1GB,另一部分为用户进程空间3GB,也就是进程空间。Windows上2GB系统内存,2GB为用户进程空间。

在Linux系统中,0x00000000-0xC0000000为进程地址空间,0xC0000000-0XFFFFFFFF为系统地址空间。

PAE (Physical Address Extension)
一个程序使用的地址空间可以超过4GB么?如果是虚拟地址,那么不行。如果是物理地址,那么可以。


?二、装载方式
程序指令需要装在内存中才能顺利运行,很多情况下程序需要的内存数量大于物理内存的数量,并且内存是很昂贵的,我们希望在不加内存的情况下让更多程序运行起来。而程序运行是有局部性原理的,我们可以将程序最常用的部分驻留在内存中,而将一些不常用的数据存放在磁盘里,这是动态装入的基本原理。

2.1 覆盖装入(Overlay)

?2.2 页映射
页映射将内存和所有磁盘的数据和指令按照 页 为单位分成若干个页,所有装载和操作的单位都是页。以目前的情况,硬件规定页的大小有4096B、8192B、2M、4M等。512M的物理内存有 512*1024*1024/4096这么多页数。



?如果需要将P4装载,那么需要做出选择,有两种算法,先进先出算法(FIFO)和最少使用算法(LUR)。

?三、从操作系统角度看可执行文件的装载
?
?3.1 进程的建立
?
?1. 创建一个独立的虚拟地址空间。
2. 读取可执行文件头,并建立虚拟空间与可执行文件的关系。
3. 将CPU的指令寄存器设置为程序的入口,启动运行。
虚拟地址空间的创建:

总结起来就是:创建页映射函数,将虚拟空间的各个页 映射至物理空间。分配一个页目录,不设置映射关系,到后面发生页错误时再进行设置。
读取可执行文件头:

?缺页时应当知道所需的页在可执行文件的哪一个位置,这便是虚拟空间与可执行文件的映射关系。这很重要。
?
?有关虚拟内存区域:

将CPU指令寄存器设置为程序入口,启动。?

3.2 页错误?
?

页错误:

四、进程虚拟空间分布?
4.1 ELF文件链接视图和执行视图
映射到进程虚拟地址空间的不值代码段,情况很复杂。

操作系统在装载程序时不关心里面的内容,只关心权限问题,权限由以下几种组合:
1. 以代码段为代表的可读可执行段
2. 以BBS和数据段为代表的可读可写段
3. 以只读数据为代表的只读段,?
 
 
?这里的segment从装载的角度重新划分了段,权限类似的放到同一个segment中,系统按照segment来进行可执行文件的映射。
加下来以一段代码为例子。
#include<stdlib.h>
int main()
{
while(1)
{
sleep(1000);
}
return 0;
}
?
?
?





4.2 堆和栈

总结一下:虚拟内存中存着管理进程的地址空间的东西,进程中的栈和堆同样存在VMA中,我们可以通过/proc来查看进程虚拟空间分布。

我们从表中可以看到5个VMA,其中前两个是映射到可执行文件的segment。另外三个没有映射到可执行文件中,?这种VAM叫匿名虚拟内存区域,我们可以看到里面的堆和栈,这几乎存在于所有的进程中。C语言中的malloc就是从堆中申请内存,堆由系统库管理。另一个vdso为内核模块,他的地址在内核空间中。

?这里继续总结,操作系统通过划分一个一个VMA来管理进程的虚拟空间,基本原则是将相同权限属性的、有相同映像文件的映射为一个VMA,一个进程基本可以区分为如下几种VMA区域:
1. 代码VMA,权限只读,可执行,有映像文件。
2. 数据VMA,权限可读写,可执行,有映像文件。
3. 堆VMA,权限可读写,可执行,无映像文件,匿名,可向上扩展。
4. 栈VMA,权限可读写,不可执行
?
?4.3 堆的最大申请数量


4.4 段地址对齐?


4.5 进程栈初始化?

 
?

?这里总结一下,程序将初始化信息压入栈中。main函数里的argc和argv分别对应命令行参数数量、命令行参数字符串指针数组。
五、Linux内核装载ELF过程的简介
当我们使用Linux的bash命令执行一个可执行文件时,系统先调用fork()形成一个新的进程,这个新的进程调用execve()来执行ELF可执行文件。原来的bash进程启动返回等待刚才启动的新进程结束,然后继续等待用户输入新的命令。execve被定义在unistd.h中:

这三个参数分别是 执行的程序文件名、执行参数、环境变量。具体如下:

?魔数再现:

下面是很复杂的过程,具体感兴趣还是去读原著吧。。。


?六、Windows的PE的装载

总结一下:就是说Windows的段都是以页为单位,段的起始地址都为4096的整数倍。这样比ELF简单许多,一个段就另起一页,用不到Segment这种概念了,并且PE文件没有那么多段,差不多有代码段、数据段、只读数据段、BSS等。

?
1. 读取文件第一页,包含DOS头、PE文件头和段表。
2. Rebasing问题
3. 使用段表中提供的信息,将PE文件中所有段 映射到地址空间中相应的位置。
4. 装载DLL
5. 解析导入符号
6. 根据PE指定参数,建立初始化栈和堆
9. 建立主线程并启动进程
 ?
小结?
介绍了可执行文件的装载过程,页映射的方式以及为什么进行页映射。介绍了虚拟地址中的分布,数据段、代码段、BSS、堆、栈等。?
|