栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

可执行文件的装载与进程

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

可执行文件的装载与进程

目录

一、进程虚拟地址空间

 二、装载方式

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
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、堆、栈等。 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/882722.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号