最近一段时间在学习linux内核,这里将进程启动的过程的一些心得体会记录一下。
1、进程启动
linux环境下我们启动一个程序一般都是通过shell拉起来的。或者通过一个程序调用exec系列函数进行进程替换的。
其实这两种方式实质是一样的,shell拉起也是调用的exec系列函数;所以我们就以第二种方式用代码来分析整个装载过程。
2、程序代码
首先我们来看下我们的实例程序代码。
为了好跟踪内核代码,所以这里通过qemu模拟了linux和文件系统。上左图为主进程,主进程fork了一个子进程,子进程最后调用exec*将自己替换为hello进程(),
hello进程代码为右图。
3、 内核代码走读
其实这里主要就是跟踪exec* 系统调用在内核中的执行,exec*系列函数都是execve的封装例程,execve系统调用最终对应的系统调用处理函数为sys_execve。
sys_execve——> do_execve——>do_execve_common调用过程,do_execve_common 里面的实际上就是在组装 struct linux_binprm结构体。
其主要调用exec_binprm。
最终找到elf的加载函数,开始正式按照elf格式进行加载。
,通过上图,可以看到“current_pt_regs”这个就是将当期进程的寄存器堆取出来(eax、ebx……)。
然后“elf_entry”这个就是新的进程的入口,也就是这里是在准备新进程的堆栈信息、执行环境,实际上start_thread就是将原来的进程的堆栈信息全给替换为了新的进程的堆栈.
4、调试跟踪
如上图可以看到在start_thread函数中将新的进程的入口函数加载到堆栈中了,而进程入口就是进行elf文件中的entry point address.于是当进程返回用户空间时,就从新的进程入口开始执行了,于是进程就被替换成新的进程了——偷梁换柱
5、总结
其实我们上面只分析了进程为静态链接的情况。如果是动态链接,老进程的堆栈入口将不是elf文件的开始。二手ld动态加载器,如下图代码所示:
我们进程的加载都是通过exec系统调用在内核中通过堆栈的替换 来偷梁换柱完成的。可以比喻为当一个人走进一个房间后(内核),等他出来时他的灵魂已经被换成另一个人了。
原文地址:https://www.cnblogs.com/tjyuanxi/p/9313253.html