可执行文件的装载与进程一点小总结 《程序员的自我修养》·笔记

可执行文件的装载与进程小结

  • 进程的虚拟地址空间
      每个程序被运行起来之后都拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小是CPU的位数决定的。比如,32位的硬件平台决定了虚拟地址空间的地址为(2^32-1),也就是我们常说的4GB虚拟内存的大小。
      需要注意的是,分配的4GB的虚拟空间并不是全部给进程的,比如,linux下1GB给操作系统,余下的3GB中基本上都分配给进程,但是3GB中的其中小部分要分配给其他用途;win下面按照2GB、2GB进行类似的划分。

  • 装载的方式

    • 装载的基本思想
        将程序最常用的部分驻留在内存。最常用的方法是页映射,如下。
    • 页映射
        要完成页映射就要将内存和磁盘中的数据和指令按“页”为单位划分成若干页,以后所有的装载和操作的单位就是页。下图就是可执行文件(虚拟空间)与物理内存的映射(不考虑程序运行的虚拟地址空间):

        关于页的操作有很多种情况,比如“内存满时的页置换”、“页错误”等等情况下采取的种种策略(FIFO、LUR)这里不再赘述。
  • 从操作系统的角度看可执行文件的装载方法

    • 进程的创建
        从操作系统的角度看,一个进程最关键的特征就是他有独立的虚拟地址空间,这使得它有别于其他进程,上述的映射关系直接使用物理地址进行操作,那么每次页装入的时候就要就行重定位,所以我们需要引入进程的虚拟运行地址空间。那么,下面就说一下从操作系统角度看一个程序被执行的大致过程:
        1.首先是创建程序对应的虚拟地址空间。即进行虚拟地址空间与程序执行的物理内存的映射(方向是进程虚拟空间到进程物理内存)。我们知道一个虚拟空间由一组页映射函数将虚拟空间各个页映射至相应的物理空间。此处所谓的“创建”并不是创建空间,而是创建虚拟空间到物理内存空间的映射函数所需要的一系列的数据结构,对于Linux就是创建一个“页目录”结构即可,并不需要设置虚拟页到物理页的映射关系。linux下将虚拟空间的各个页映射至相应的物理空间,实际上只是分配了一个页目录(Page Directory)就可以了,并且不用设置页映射关系,这些映射关系到后面程序发生页错误的时候再进行设置。
        2.读取可执行文件头,建立进程虚拟地址空间和可执行文件的映射关系。这一步将可执行文件空间与虚拟空间关联起来(方向是可执行文件虚拟空间到进程虚拟空间),使得发生缺页错误时,OS能够知道到可执行文件中的哪个位置去找到所需要加载到物理内存的内容;这种映射关系只是保存在操作系统内部的一个数据结构。Linux中将进程虚拟空间中的一个段叫做虚拟内存区域(VMA,Virtual Memory Area);在Windows中将这个叫做虚拟段(Virtual Section)。
        【注意】由于可执行文件在装载的时候实际上是被映射的虚拟空间,所以可执行文件很多时候被称作映射文件。进程虚拟地址空间和可执行文件的映射关系如下:

        3.设置CPU的指令寄存器为可执行文件的入口地址,启动运行:OS将控制权交给了进程。从进程的角度看这一步可以简单的认为操作系统执行了一条跳转指令,直接跳转到可执行文件的入口(ELF文件头中保存了入口地址项)。
    • 页错误
        完成上述三个步骤之后,其实OS仅仅只是可执行文件与进程虚存之间建立起了映射——即通常意义上所说的程序加载到了内存,实际上这里说的是程序完全加载到了虚拟内存,但是代码和数据根本就没有加载到物理内存中,进程虚存与物理内存空间的映射关系其实也没有建立起来(上面也说了在“页错误阶段进行映射关系的设置”),这样程序一旦开始执行,将会立即出现缺页错误,即程序将要访问的进程虚存地址并没有映射到物理内存空间的某个page(虚拟页),(页错误的处理线程执行)此时OS会重新接管系统控制权,查询刚才第二步保存的可执行文件到进程虚存映射关系的数据结构,找到所缺的虚拟页对应于可执行文件中的偏移,然后在进程物理内存分配一个物理页,将可执行文件中的内容从磁盘读入到内存中,并将这个物理页(进程物理内存)与该虚拟页(进程虚存)建立起映射,然后OS将控制权重新交给进程,程序继续执行。如下图所示:
  • 进程虚存空间分布

    • ELF文件在映射到进程虚存的过程中是以系统的页作为单位的,那么每个段在映射时的长度都应该是系统页长度的整数倍;如果不是那么多余部分也将占用一页。这样的话内存浪费是大问题。
    • ELF文件中, 段的权限只有为数不多的几种组合:
      1.以代码段为代表的权限为可读可执行的段
      2.以数据段和BSS段为代表的权限为可读可写的段
      3.以只读数据段为代表的权限为只读的段。
      对于相同权限的段,把它们合并到一起当作一个段进行映射。如下图,".text"和".init"段都是可读可执行的,则进行合并,形成一个"segment":


    • 堆和栈
      kernel使用VMA划分来管理进程的虚拟地址空间。典型的进程包括代码:
      1.代码VMA(RE属性,有映像文件)
      2.数据VMA(RWE属性,有映像文件)
      3.堆VMA(RWE属性,无映像文件,向上扩展)
      4.栈VMA(RW属性,无映像文件,向下扩展)
      如下图所示:



      【需要注意】其实DATA segment对应的就是DATA VMA;CODE segment对应的就是CODE VMA。几乎在每一个进程的VMS视图中都可以看见[heap]和[stack]这两个VMA,但是这两个VMA在可执行文件中都没有对应的segment存在,所以它们被称之为匿名VMA。malloc()库函数就是从堆VMA中分配空间。

  • Linux内核装载ELF过程
      Linux环境下,fork系统调用将会创建一个与当前task完全一样的新task,直到应用程序调用exec*系列的Glibc库函数最终调用execve()系统调用之后,Linux内核才开始真正装载ELF可执行文件(映像文件)。execve内核入口为sys_execve(),随之调用do_execve()将查找这个可执行文件,如果找到则读取ELF可执行文件的前128个字节,然后调用search_binary_handle()通过ELF文件头中的e_ident得到可执行文件的Magic Number,判断出这是一个什么类型的可执行文件,并调用不同可执行文件的装载处理程序,对于ELF可执行文件而言,其装载处理程序为load_elf_binary(),这个函数将会把execve系统调用的返回地址修改为ELF可执行文件的入口点,对于静态链接得到的ELF文件即文件头中定义的e_entry,对于动态链接得到的ELF可执行文件则是动态链接器。一步一步返回到sys_execve()之后,因为返回地址已经被修改为了ELF程序入口地址了,所以系统调用返回到用户态之后,EIP指令寄存器将直接跳转到ELF程序入口地址,程序开始执行,装载完成。
    ELF文件的装载过程:

    fork -> execve() -> sys_execve() -> do_execve()

    do_execve() 读取文件的前128个字节判断文件的格式(一般根据魔数来判断,比如elf的头四个字节为:0x7F, e, l, f)。
      然后调用search_binary_handle()去搜索和匹配合适的可执行文件装载处理过程,对于elf则调用load_elf_binary():

    • 检查ELF可执行文件格式的有效性
    • 寻找动态链接的“.interp”段,设置动态连接器路径
    • 根据ELF可执行文件的程序头表的描述,对ELF文件进行映射,比如代码、数据、只读数据。
    • 根据ELF进程环境,比如进程启动是EDX寄存器的地址应该是DT_FINI的地址。
    • 将系统调用的返回地址修改成ELF可执行文件的入口点,这个入口点取决于程序的链接方式,静态ELF可执行文件为e_entry所指的地址,对于动态ELF入口点为动态连接器。

    Load_elf_binary()执行完毕,返回至do_execve()再返回至sys_execve(),最后一步的系统调用返回地址改成了被装在的ELF程序入口地址。当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到了ELF程序的入口地址,新程序开始执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容