• 进程的创建与可执行程序的加载


    进程的创建与可执行程序的加载-总结

    根据前两篇博文中的实验

    1:Linux进程地址空间之初探:一

    2:Linux进程地址空间之初探:二 

    现对进程的创建和可执行程序的加载过程总结如下:

    一:进程地址空间的组成及相关数据结构

     

           Linux为每个进程都维持了一个独立的虚拟地址空间,进程地址空间又被分为几个虚拟内存区域:代码段、数据段、堆段、栈段、共享库段。进程地址空间中的任何有效地址都只能位于唯一的区域,这些区域不能相互覆盖。通过mmap/munmap函数,内核可以创建/删除一个虚拟内存区域。

     

             主要的数据结构有

            task_struct : 进程描述符结构,定义在<linux/sched.h>文件中,描述了该进程打开的文件、进程的地址空间、进程的状态等信息。

            mm_struct :进程虚拟内存描述符,定义在<linux/sched.h>文件中,该结构包含了和进程地址空间相关的全部信息。

            vm_area_struct:虚拟内存区域描述符,定义在<linux/mm.h>文件中,该结构体描述了指定地址空间内连续区间上的一个独立的内存范围(比如进程用户空间栈、代码段、数据段等)。

            三个结构之间的关系

            task_struct 中的一个 字段 mm 指向当前进程的虚拟地址空间描述符 mm_struct , mm_struct中的字段 mmap 指向一个vm_area_struct组成的链表,其中每个vm_area_struct 都描述了当前虚拟地址空间的一个区域。

            这里涉及的一些关键结构的具体信息可查看我前面的博文 :Linux进程地址空间之初探:一

     

    二:fork 和 exec 函数及加载器的执行过程

             fork( )函数通过拷贝当前进程创建一个子进程,子进程与父进程的区别仅仅在在于PID、PPID和某些资源和统计量(例如,挂起的信号,它没必要被继承),Linux中fork是通过clone( )实现的,clone( )再调用do_fork( )。

             执行过程:

         1)为新进程创建一个内核栈、thread_info结构和task_struct 结构,这些结构的值与当前进程的值相同。此时子进程和父进程的描述符是完全相同。

         2)子进程开始使自己与父进程区别开来。进程描述符内的许多成员要被清零或设为初始值。

         3)子进程的状态被设置为TASK_UNINTERRUPTIBLE,以保证不会透入运行。

         4)设置task_struct中的相应flags标志,例如权限

         5 ) 为新进程分配一个有效PID

         6)根据传递给clone( )的参数标志进行拷贝或者共享打开的文件、文件系统信息、进程地址空间和命名空间等。

         7)最后返回子进程的PID。

     

           exec( )函数:exec函数在当前进程的上下文中加载并运行一个新的程序,只有在出错的情况下exec 函数才会返回到调用程序中,所以与fork函数调用一次返回两次不同,exec调用一次并从不返回

           exec( )执行过程:

          1)删除当前进程虚拟地址空间的用户部门已经存在的区域结构。

          2)加载可执行文件,用可执行文件中的内容覆盖当前进程地址空间相应区域

          3)设置程序计数器即eip中的值,使它指向新的代码区的入口点,调用启动代码,启动代码设置栈,控制传给新程序的主函数。

     

         其中加载可执行文件的执行过程如下:

     

          1)启动加载器,加载器删除子进程现有的虚拟地址段

          2)加载器根据可执行目标文件中的段头部表信息,创建一组新的代码段、数据段、堆段和栈段。新的堆、栈段初始化为零。代码段和数据段映射为可执行文件的代码段和数据段。

          3)根据可执行文件 ELF 中的.interp段查找动态链接器ld.so的路径名,动态链接器实际上也是一个共享对象,加载器同样通过映射的方式将它加载到进程的地址空间。然后把控制权交给动态链接器的入口地址(与可执行文件一样,共享对象也有入口地址),当动态链接器得到控制权后,进行一系列初始化操作,然后根据可执行文件ELF中.dynamic段,这个段里保存了动态链接器所需要的相关信息,比如依赖于哪些共享对象(例如libc.so)、动态链接符号表位置、动态链接重定位表的位置、共享对象初始化代码的地址等信息,根据它们查找和加载可执行文件所依赖的共享对象,并映射到进程地址空间的共享区域中。

          4)当所有动态链接工作完成以后,动态连接器会将控制权交给可执行文件的入口地址,即跳转到可执行文件的_start 启动代码并调用新程序中的main函数开始执行。

       这里提到的 .interp 、.dynamic、段头部表的查看方法及具体信息可查看我上篇博文:Linux进程地址空间之初探:二 

     

    三:task_struct进程控制块,ELF文件格式与进程地址空间的联系

         task_struct进程控制块中的mm字段所指向的mm_struct结构描述了进程地址空间的信息,包括代码段、数据段、堆段、栈段所在地址空间里的起始和结束地址等信息。

          ELF文件格式中的 ELF头部、段头部表、.init、.text、.rodata段对应进程地址空间中的代码段,在加载可执行文件时,会把它们映射到进程地址空间中的代码段区域。

          ELF文件格式中的 .data、.bss段 对应 进程地址空间中的 数据段,在加载可执行文件时,会把它们映射到进程地址空间的数据段区域。

          具体的对应关系图请见 上篇博文:Linux进程地址空间之初探:二 

     

    四:动态链接库在ELF文件格式中与进程地址空间中的表现形式

        动态链接库在ELF文件中对应着.dynamic段所包含的信息:包括动态链接器所需要的相关信息,比如依赖于哪些共享对象(例如libc.so)、动态链接符号表位置、动态链接重定位表的位置、共享对象初始化代码的地址等信息。

         动态链接库最后被映射到进程地址空间的共享库区域段。

         ELF文件格式图、可执行文件加载完后进程地址空间的状态图、及dynamic段的信息见上篇博文:Linux进程地址空间之初探:二

        

     

     

     
     
  • 相关阅读:
    PHP+MySQL
    Appstore排名前十的程序员应用软件
    架构师的平凡之路
    程序员,如何三十而立?
    不懂技术也可以轻松开发一款APP
    php语法学习:轻松看懂PHP语言
    你真的了解软件测试行业吗?
    十个程序员必备的网站推荐
    从更高点看软件开发的侧重点
    php如何实现文件下载
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3104968.html
Copyright © 2020-2023  润新知