第6章 可执行文件的装载与执行

 可执行文件的装载与执行

一、进程虚拟地址空间

        区分“程序虚拟地址空间的寻址范围”、“程序可以使用的虚拟地址空间”和“程序可以使用的内存空间”:

       1. “程序虚拟地址空间的寻址范围”、“程序可以使用的虚拟地址空间”:

            32位CPU只能使用32位指针。最大寻址范围是0~4GB;在Linux下最多可以使用3GB空间(1GB留给OS),在Windows下最多可以使用2GB(2GB留给OS)或3GB(1GB留给OS)

       2. “程序可以使用的内存空间”:

            Intel从1995年的Pentium Pro CPU开始使用36位地址线(可访问高到64GB物理内存),程序虽然还是只有32位的地址空间,但是可以从高于4GB的物理空间中申请多个大小为256MB的物理空间块。这种地址扩展称“PAE (Physical Address Extension)”.

二、装载的方式

       1. 覆盖装入(已淘汰):需要程序员手工将模块按照它们之间的调用依赖关系组织成树状结构。

 

       2. 页映射

           下图中P0~P7是“进程虚拟地址空间”中的“虚拟页”(VP, Virtual Page),右边F0~F3是“内存”中的“物理页”(PP, Physical Page)。“OS”中的“存储管理器”需要管理调入缺少虚拟页到物理页等。

       

        查看“页的大小”: $getconf PAGE_SIZE

        查看“OS位数”:$getconf LONG_BIT

        查看“CPU位数”: $arch     ==> i686或x86_64则表明机器是64位的

三、从操作系统的角度看可执行文件

      由于使用了“分页机制” ,所以提到“可执行文件的装载”并不是一次性完成的,而是分了两步:

  

      1. 进程建立时,为“分页装载”做了一些准备,还未真正装载; (1)(2)(3)

                (1)分配一个页目录Page Directory,用来记录Virtual Page(进程虚拟地址空间中)到Physical Page(内存中)的映射关系。等执行时发生缺页(Page Fault)时,由OS安排处理调用、填充页目录等。

                 (2)在进程数据结构中构造一些VMA(Virtual Memory Area),VMAs保存了Disk Page(磁盘上的可执行文件中)到Virtual Page(进程虚拟地址空间中)的映射关系。注:VMA是OS内部的一个数据结构。

                (3)将CPU指令寄存器设置为可执行文件入口,启动运行

  

      2. 缺页(产生页错误)时,才真正地去装载。(4)

                (4)缺少进程虚拟空间的某个VP时,

                                   分配一个物理页PP;

                                   查询VMA,将该VP对应的DP装入PP;

                                   在目录表Page Dir中添加一个“VP->PP”的对应关系。

四、进程虚存空间分布

      1. ELF文件的链接视图和执行视图

          ELF文件中,段(Section)的权限往往只有以下三种

                (1) 可读可执行的 代码段; (2) 可读可写的 数据段和BSS段; (3) 只读的 只读(数据)段。

          在装载ELF可执行文件时,对于相同权限的Section,把它们合并为一个Segment进行映射。ELF可执行文件中有一个专门的数据结构用来保存Segment的信息,叫做“程序头表 Program Header Table”。因为ELF目标文件不需要被装载,所以没有程序头表;而ELF可执行文件和共享库文件都有程序头表。

          Segment和Section是从不同角度划分同一个ELF文件。从Section的角度来看ELF文件就是链接视图Linking View;而从Segment的角度来看就是执行视图Execution View

          

      2. 

五、Linux内核装载ELF过程

       在Linux的bash下输入一个命令执行某个ELF程序时,

       (1) bash进程会调用fork()系统调用创建一个新的进程;

       (2) 新的进程调用int execve(const char* filename, char* const argv[], char* const envp[])系统调用执行指定的ELF文件。

       下面使用fork()和execve()实现一个简易的bash:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
	char buf[1024]={0};
	pid_t pid;

	while(1){
		printf("minibash$");
		scanf("%S", buf);
		pid=fork();
		
		if(pid==0){
			//是对execve()的封装,执行指定的ELF可执行文件
			if(execlp(buf, 0)<0){
				printf("exec error\n");
			}
		}else if(pid>0){
			int status;
			waitpid(pid, &status, 0);			
		}else{
			printf("fork error %d\n", pid);
		}
	}
}

 注:在这里可以见fork()详解——http://blog.csdn.net/jason314/article/details/5640969

六、Windows PE的装载

猜你喜欢

转载自chuanwang66.iteye.com/blog/1773385