前情提要
- 这片博客纯属为自己做个笔记,其中还有很多解释不到位的地方,特别是后面对底层源代码的分析,可能会有解释不恰当甚至不合理的地方。但我会在后面对源码有了更深层次的研究之后对这篇博客里的源码分析进行修改。还是一样,这篇博客还有很多的知识部分来自《程序员的自我修养》
Linux内核中的ELF文件装载过程之详解execve
———部分取材自《程序员的自我修养》
一:知识储备
- 从用户态到内核态的切换过程
从宏观上来看,Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核)。内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程序运行的环境。用户态即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。
以上摘自博客:
https://www.cnblogs.com/bakari/p/5520860.html
知道了用户态和内核态的概念,我们来看一下这两种状态的转换:
- 为什么要进行这种转换:
原因在于,运行于用户态的进程可以执行的操作和访问的资源都会受到极大的限制,而运行在内核态的进程则可以执行任何操作并且在资源的使用上没有限制。所以在执行某些特权级别较高的操作的时就会涉及到这种转换。 - 两种模式转换的情景:
系统调用,异常事件,外围设备的中断。这里重要对系统调用做一个说明。 - 系统调用的过程:
在系统需要调用一个系统调用函数时,例如在执行函数execve这个函数时,第一步需要调用sys_exece函数,这个过程可以用一张图来解释:(这里是从宏观角度做大概流程的示意)
二:ELF的装载过程
- 我们以shell中的bash来进行这个过程的串联。
首先在用户层面,在用户输入一的命令后,bash进程首先会调用fork();系统调用来创建一个新的进程。然后新的进程会调用execve();系统调用执行指定的ELF文件,原先的bash进程继续返回等待刚才的新进程结束,然后继续等待用户输入命令。
一: execve();底层执行的过程
execve();被定义在unistd.h 函数原型为:
int execve(cosnt char *filename,char *const argv[ ],char *const envp[ ]);
它的三个参数分别是被执行的程序文件名,执行参数和环境变量。在进入execve();系统调用之后,Linux内核就开始进行真正的装载工作。
- 在内核中,execve();系统调用的相应入口是sys_execve();他被定义在arch\i386\kerne\Process.c,其函数原型为:
int sys_execve(char *filenamei,
char **argv,
char **envp,
struct pt_regs *regs);
其功能是进行一个参数的检查复制后调用do_execve();
int do_execve(char * filename,
char __user *__user *argv,
char __user *__user *envp,