一、实验过程
1.打开控制台,进入Linux目录下,然后输入命令rm menu -rf 删除menu,然后输入命令Git clone https://github.com/mengning/menu.git 重新克隆一个menu,实验结果如图所示。
2.用test_exce.c 代替test.c
3.输入make rootfs对程序进行编译,并启动MENUOS,如图所示。
4.使用gdb来调试
5.在gdb中输入命令 b execve,b load_elf_binary,b start_thread设置断点,然后利用gdb进行跟踪调试。实验结果如下图所示。
6. 在menuos的命令提示符下输入exec命令,可以发现exec命令卡住了如下图所示
7. 此时可以在gdb调式窗口继续输入c发现gdb停留在load_elf_binary断点处,输入list命令可以查看断点处相应的代码。继续输入c命令,程序执行到start_thread断点处,如下图所示
8. 接着输入s可以一步一步跟踪发现start_thread是如何设置进程内核堆栈的EIP的,实验及图如下所示
以上只是设置了exeve系统调用过程中比较重要几个断点进行跟踪调试,其实我们还可以设置更为详细的断点来仔细了解内核是如何加载和启动可执行程序的。
二、分析exec*函数对应的系统调用处理过程
在使用exec*函数的时候,一般首先需要调用一个fork来生成一个新的子进程(否则原有的进程将会被覆盖掉),然后新的进程调用execve()系统调用来执行指定的ELF文件。内核调用sys_execve函数来实现execve。
sys_execve通过调用 do_execve_common,首先访问需要加载文件所在的目录文件,然后通过search_binary_handle在目录中检索需要执行的文件,并根据文件类型来采用对应的加载函数对其进行加载。在加载的过程中,将原来进程的代码段、以及堆栈等利用所加载的文件中的对应值进行替换,最后重新设定EIP和ESP来使可执行文件运行起来。
至于运行参数以及环境变量,则是首先传递到系统调用,然后传递到一个struct linux_binprm类型的结构体中,最后,该结构体变量作为参数参与load工作,将程序的运行参数以及环境变量参数应用到可执行程序上。
陈涛 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”