Linux 核心--5.Linux进程( 八 )



可执行文件可以有许多格式 , 甚至是一个脚本文件 。脚本文件需要恰当的命令解释器来处理它们;例如 /bin/sh解释shell脚本 。可执行目标文件包含可执行代码和数据 , 这样操作系统可以获得足够的信息将其 加载到内存并执行之 。Linux最常用的目标文件是ELF , 但是理论上Linux可以灵活地处理几乎所有目标文件 格式 。




图4.3 已注册的二进制格式


通过使用文件系统 , Linux所支持的二进制格式既可以构造到核心又可以作为模块加载 。核心保存着一个可以支持的二进制格式的链表(见图4.3) , 同时当执行一个文件时 , 各种二进制格式被依次尝试 。

Linux上支持最广的格式是a.out和ELF 。执行文件并不需要全部读入内存 , 而使用一种请求加载技术 。进程 使用的可执行映象的每一部分被调入内存而没用的部分将从内存中丢弃 。


4.8.1ELF
ELF(可执行与可连接格式)是Unix系统实验室设计的一种目标文件格式 , 现在已成为Linux中使用最多的格式 。但与其它目标文件格式相比 , 如ECOFF和a.out , ELF的开销稍大 , 它的优点是更加灵活 。ELF可执行文件 中包含可执行代码 , 即正文段:text和数据段:data 。位于可执行映象中的表描叙了程序应如何放入进程的 虚拟地址空间中 。静态连接映象是通过连接器ld得到 , 在单个映象中包含所有运行此映象所需代码和数据 。此映象同时也定义了映象的内存分布和首先被执行的代码的地址 。






图4.4 ELF可执行文件格式

图4.4给出了一个静态连接的ELF可执行映象的构成 。

这是一个打印"Hello World"并退出的简单C程序 。文件头将其作为一个带两个物理文件头(e_phnum = 2)的ELF映象来描叙 , 物理文件头位于映象文件起始位置52字节处 。第一个物理文件头描叙的是映象中的可执行代码 。它从虚拟地址0x8048000开始 , 长度为65532字节 。这是因为它包含了printf()函数库代码以输出"Hello World"的静态连接映象 。映象的入口点 , 即程序的第一条指令 , 不是位于映象的起始位置 而在虚拟地址0x8048090(e_entry)处 。代码正好接着第二个物理文件头 。这个物理文件头描叙了此程序使用的数据 , ? 患釉氐叫槟饽诖嬷?x8 的大小在文件中是2200字节(p_filesz)但是在内存中的大小为4248字节 。这是因为开始的2200字节包含的是预先初始化数据而接下来的2048字节包含的是被执行代码初始化的数据 。

当Linux将一个ELF可执行映象加载到进程的虚拟地址空间时 , 它并不真的加载映象 。首先它建立其虚拟内存数据结构:进程的vm_area_struct树和页表 。当程序执行时将产生页? bf9 娲恚鸪绦虼牒褪荽游锢砟诖嬷腥〕觥3绦蛑忻挥惺褂玫降牟糠执永炊疾换峒釉氐侥诖嬷腥ァR坏〦LF二进制格式加载器发现这个映象是有效的ELF可执行映象 , 它将把进程的当前可执行映象从虚拟内存冲刷出去 。当进程是一个复制映象时(所有的进程都是) , 父进程执行的是老的映象程序 , 例如象bash这样的命令解释器 。同时还将清除任何信号处理过程并且关闭打开的文件 , 在冲刷的最后 , 进程已经为新的可执行映象作好了准备 。不管可执行映象是哪种格式 , 进程的mm_struct结构中将存入相同信息 , 它们是指向映象代码和数据的指针 。当ELF可执行映象从文件中读出且相关程序代码被映射到进程虚拟地址空间后 , 这些指针的值都被确定下来 。同时vm_area_struct也被建立起来 , 进程的页表也被修改 。mm_struct结构中还包含传递给程序和进程环境变量的参数的指针 。


ELF 共享库

推荐阅读