Linux内核如何装载和启动一个可执行程序

黄志恒
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

这篇文章来讨论一下一个可执行程序在linux内核中如何装载和启动的。
首先,来科普从代码文件变为可执行文件的流程:
以C语言文件为例:.c文件——(预处理和编译)——>.asm的汇编文件——(链接)——>.o的目标文件——(装载)——>a.out的可执行文件——(执行)——>进入内存。
用图示来表示是这样子的:
这里写图片描述
这张图省略了预处理这一步,这里提示一下。

这里有一个地方需要科普一下:在链接的时候分为动态链接和静态链接。
静态链接是把程序所需的库文件在链接的时候都拷贝下来和程序文件放在一个目标文件中。静态链接出来的目标文件占用空间大,但是执行效率高。
动态链接是程序在执行的时候需要用到哪个库文件就去库文件目录找,不用在执行前和目标文件链接到一起。动态链接出来的目标文件占用空间小,但是执行效率相比静态链接的稍低。
动态链接也分可执行程序装载时动态链接运行时动态链接。二者在用gcc编译时的指令是相同的,但是使用的方式略有不同。我们平时编程大多使用的事装载时动态链接。

还有一个小知识点:编译成的.o目标文件以及链接成的可执行文件都是二进制的,并且现在绝大多数使用ELF格式。这个ELF格式规定了文件中不同数据的存放位置,比如文件头放哪里,链接的库文件地址放哪里,代码段放哪里,数据段放哪里等等。当程序执行时,就可以通过逐步解析ELF文件来获取要执行的内容了。

ELF格式文件再细分一点可以分为如下三个:
A:可重定位文件.o,该文件保存着代码和适当的数据机构。用来和其他的目标文件一起来创建一个可执行文件或者一个共享文件
B:可执行文件,该文件保存着一个可执行程序。该文件指出了exec如何来创建进程的映像。
C:共享目标文件.so:该文件保存着代码和合适的数据。该文件用来被2个连接器链接。一个是链接编辑器,可以和其他的可重定位文件和共享目标文件来创建其他的目标文件。另一个是动态链接器,联合一个可执行文件和其他的共享目标文件来创建一个进程映像。

接下来,分析可执行文件从在shell中输入指令到程序执行的过程。
首先,在shell中输入可执行程序的指令后面是可以添加参数的。包括命令参数和环境参数。这些参数可以传递到执行程序中,执行程序根据参数做不同业务。shell本身不限制参数的个数,由执行程序的main函数来限制参数个数。这里有一个点是值得注意的:参数是在当前进程输入的,新程序执行是在另一个进程中执行的。这里先经过函数调用传递参数,再用系统调用传递参数。这样参数就可以传递到新进程中了。

接下来就是系统调用的过程了,新程序的执行用到的系统调用是sys_execve。这里面的流程是do_execve–>do_execve_common–>exec_binprm。具体的参考sys_execve源码:http://codelab.shiyanlou.com/xref/linux-3.18.6/fs/exec.c#1604

这流程里面关键的地方有这些:
load_eif_binary:解析ELF格式的二进制文件,就是解析可执行文件。
解析完了,执行start_thread就可以开始执行新的程序了。

这里有这么一个问题:在父进程中执行新程序,进入内核态,从内核态返回,竟然返回到子进程中了!
上一篇文章讲过,新进程的结构是从父进程中拷贝下来修改后成为自己的。这里通过在load_elf_binary中设置eip的值,使从内核态返回时返回到子进程的进程中。

下面通过无所不能的menuOS来演示一下上面上所说的内核调用的流程。
这里写图片描述
这张图是在menuOS中执行exec指令后,停在sys_execve 中断点的输出。

这里写图片描述
这张图是在sys_execve后继续执行,停在load_elf_binary的中断点的输出。可以看到menuOS并没有继续输出新的数据。说明load_elf_binary是sys_execve流程一部分。

这里写图片描述
这张图是在load_elf_binary后继续执行,停在start_thread的中断点得输出。可以看到menuOS仍然没有继续输出新的数据。

这里写图片描述
这张图是在start_thread之后继续执行,可见从start_thread后,新程序开始执行。

上面内容的实验环境和menuOS相关内容可以进入这个网页进行学习:http://mooc.study.163.com/learn/USTC-1000029000#/learn/content?type=detail&id=1000130000&cid=1000116191

总结:
Linux内核在装载和启动一个可执行程序时,经历了父进程用户态——>内核态——>子进程用户态这么一个转变。其中,内核态要做的主要工作就是解析ELF格式的可执行程序文件。依据这个解析为子进程程序的运行准备条件。在条件准备完善后,通过修改eip的值,使CPU执行新程序。这一整套流程逻辑过程清晰,但是源码很复杂,有很多的细节需要系统设计者考虑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值