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

2. 实验步骤

与上一课的实验步骤基本相同,在shell中依次运行以下命令,并编译运行

cd LinuxKernel

rm menu -rf

cd menu

mv test_exec.c test.c

make rootfs

a4c26d1e5885305701be709a3d33442f.png

重新回到shell窗口,cd LinuxKernel回退到LinuxKernel目录,使用下面的命令启动内核

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

-s -S

水平分割一个新的shell窗口出来,使用下面的命令启动gdb调试

gdb

(gdb) file linux-3.18.6/vmlinux

(gdb) target remote:1234

并在系统调用sys_execve的入口处设置断点

(gdb) b sys_execve

在QEMU窗口中输入exec,系统就会停在上面设置的断点处:

a4c26d1e5885305701be709a3d33442f.png

同理可以设置以下断点

b load_elf_binary

b start_thread

从而追踪内核代码。

3.理解Linux系统加载可执行程序所需处理过程

理论:

Linux中,从c源代码到一个可执行程序,这其中要经过预处理、编译和链接的过程。

可通过课堂中的例图理解

a4c26d1e5885305701be709a3d33442f.png

主要目标文件的格式:ELF(课堂例图)

a4c26d1e5885305701be709a3d33442f.png

ELF的三种类型目标文件:

ELF文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型:

可重定位的目标文件(Relocatable,或者Object

File):保存代码和适当数据,和其它Object文件一起创建可执行文件或共享文件。

可执行文件(Executable):文件保存一个用来执行的程序,该文件指出exec如何来程序进程映像。

共享库(Shared Object,或者Shared

Library):保存代码和合适的数据,用来被连接编辑器和动态链接器进行链接。

ELF目标文件参与程序的链接和执行。ELF头文件里保存了文件的组织情况,告诉系统如何创建一个进程的内存映象。

当通过用父进程调用fork创建一个新进程时,系统实际上是拷贝了父进程的一个文件段和虚拟了一个内存段。虚拟内存是假想的内存,它其实是不存在的,而仅仅是由一些硬件和软件管理的一种“系统”。他提供了三个重要的能力:1,它将主存看成一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据(这里存在“交换空间”以及“页面调度”等概念),通过这种方式,高效地利用主存;2,它为每个进程提供了统一的地址空间(以虚拟地址编址),从而简化了存储器管理;3,操作系统会为每个进程提供独立的地址空间,从而保护了每个进程的地址空间不被其他进程破坏。

静态链接的ELF可执行文件与进程的地址空间:

a4c26d1e5885305701be709a3d33442f.png

装载可执行文件

LINUX

一般通过shell程序为执行环境来启动一个可执行程序。Shell本身不限制命令行参数的个数,它受限于命令自身;Shell会调用一个系统调用exece将命令参数和环境参数传递给可执行程序的main函数。

命令行参数与环境变量的保存与传递:

当fork一个子进程时,是先复制父进程,再调用exece,会把原来的进程环境覆盖掉,用户态堆栈也被清空。用户态堆栈以start_stack作为main函数的起点,把argv[

]命令行参数 和envp[

]环境变量的内容通过指针的方式传递到系统调用exeve(内核处理函数);exeve创建一个新的用户态堆栈时把上面的命令行参数与环境变量拷贝到新的用户态堆栈里,从而初始化新的可执行程序的上下文环境。exece在内核态下装载可执行程序,再返回用户态。所以它先进行函数调用参数传递,然后系统调用参数传递,最后又进行函数调用参数传递。

动态链接

动态链接是相对于共享对象而言的。动态链接器将程序所需要的所有共享库装载到进程的地址空间,并且将程序汇总所有为决议的符号绑定到相应的动态链接库(共享库)中,并进行重定位工作。

动态连接有两种形式:可执行程序装载时动态连接和运行时动态链接

4. 理解总结可执行程序的装载过程 :

在进入execve()系统调用之后,Linux内核就开始进行真正的装载工作。在内核中,execve()系统调用相应的入口是sys_execve(),作用:参数的检查复制;调用do_execve(),流程:查找被执行的文件,读取文件的前128个字节以判断文件的格式是elf还是其它;调用search_binary_handle(),流程:通过判断文件头部的魔数确定文件的格式,并且调用相应的装载处理程序。ELF可执行文件的装载处理过程叫load_elf_binary(),它的主要步骤如下:1,检查ELF可执行文件格式的有效性,比如魔数、程序头表中段的数量。

2,寻找动态链接的“.interp”段,找到动态链接器的路径,以便于后面动态链接时会用上。

3,读取可执行文件的程序头,并且创建虚拟空间与可执行文件的映射关系:创建虚拟空间时的页映射关系函数是虚拟空间到物理内存的映射关系,而这一步所做的事虚拟空间与可执行文件的映射关系。我们知道,当程序发生缺页是,操作系统会为物理内存分配一个物理页,然后将该缺页从磁盘中读取到内存,在设置缺页的虚拟页与物理页之间的映射关系,这样程序才可以得以正常运行。但是明显的一点是,当操作系统捕获到缺页错误时,他应当知道程序当前需要的页在可执行文件中的哪一个位置。而这就是虚拟存储与可执行文件之间的映射关系。实际上,这种映射关系仅仅是保存在操作系统内部的一个数据结构。当发生缺页错误是,CPU将控制权交给操作系统,操作系统利用专门的缺页处理例程来查询这个数据结构(映射关系),然后找到所需页所在的虚拟内存区域,以及在可执行文件的偏移,然后把该页加载进物理内存,同时将该虚拟页与物理页之间建立映射关系,最后把控制权还给进程,进程从刚才缺页位置重新开始执行。

4,初始化ELF进程环境。

5,将系统调用的返回地址修改成ELF可执行文件的入口点,这个入口点取决于程序的链接方式,对于静态链接的ELF可执行文件,它就是ELF文件的文件头中e_entry所指的地址;对于动态链接的ELF可执行文件,程序入口点就是动态链接器。

【将CPU指令寄存器设置成可执行文件的入口,启动运行】对动态链接来讲,此时就启动了动态链接器。

当load_elf_binary()执行完毕,返回至do_execve()在返回至sys_execve()时,系统调用的返回地址已经被改写成了被装载的ELF程序的入口地址了。所以,当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到ELF程序的入口地址。此时,ELF可执行文件装载完成。接下来就是动态链接器对程序进行动态链接了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值