1.父进程的行为: 复制,等待
执行应用程序的方式有很多,从shell中执行是一种常见的情况。交互式shell是一个进
程(所有的进程都由pid号为1的init进程fork得到,关于这个话题涉及到Linux启动和初
始化,以及idle进程等,有空再说),当在用户在shell中敲入./test执行程序时,shell先
fork()出一个子进程(这也是很多文章中说的子shell),并且wait()这个子进程结束,所以
当test执行结束后,又回到了shell等待用户输入(如果创建的是所谓的后台进程,shell
则不会等待子进程结束,而直接继续往下执行)。所以shell进程的主要工作是复制一
个新的进程,并等待它的结束。
2.子进程的行为: "执行"应用程序
2.1 execve()
另一方面,在子进程中会调用execve()加载test并开始执行。这是test被执行的关键,下
面我们详细分析一下。
2.2 do_execve()
Execve()->do_sys_execve()->do_execve()(check if file exist and if can be runed by current user)
2.3 search_binary_handler()
建立可知性文件列表struct linux_binfmt,遍历执行。
2.4 load_elf_binary()
加载elf类型文件的handler是load_elf_binary(),它先读入ELF文件的头部,根据ELF文件
的头部信息读入各种数据(header information)。
2.5 load_elf_interp()
如果应用程序中使用了动态链接库,就没有那么简单了,内核除了加载指定的可执
行文件,还要把控制权交给动态连接器(program interpreter,ld.so in linux)以处理动态
链接的程序。
简短的说,整个在shell中键入./test执行应用程序的过程为:当前shell进程fork出一个
子进程(子shell),子进程使用execve来脱离和父进程的关系,加载test文件(ELF格式)到
内存中。如果test使用了动态链接库,就需要加载动态链接器(或者叫程序解释器),
进一步加载test使用到的动态链接库到内存,并重定位以供test调用。最后从test的入
口地址开始执行test。
PS: 现代的动态链接器因为性能等原因都采用了延迟加载和延迟解析技术,延迟加
载是动态连接库在需要的时候才被加载到内存空间中(通过页面异常机制),延迟解
析是指到动态链接库(以加载)中的函数被调用的时候,才会去把这个函数的起始地
址解析出来,供调用者使用。