在终端敲入./a.out 的执行流程图
一.fork()
1.shell进程调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。
2.子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
3.fork执行流程如下:
大致流程如下:
1.分配一个task_struct
2.拷贝task_struct,深度拷贝task_struct中的成员:打开文件信息、文件系统信息、虚拟内存及映射表信息。
3.子进程虚拟堆栈空间初始化
4.设置子进程的调度状态
5.返回PID
二、execve
1. execve为系统调用api,通过调用execve(程序名)可以加载和启动新程序.
2.sys_exece(),检查参数程序参数argv, 环境变envp
3.do_execve(),获取magic number 等信息,共128字节,判断文件格式与类型
4.search_binary_handler(),利用前面获取的信息,调用相对应的handler,如:load_scrpt()、load_elf_binary()
5.load_elf_binary()
i. 获取 program header
ii. 如果是动态链接库则利用。interp section 确定loader路径
iii. 将program header记录的地址信息映射到内存中,映射信息在《进程分析之旅02之可执行文件ELF格式分析》的结尾处有分析
iv. 将sys_execve的返回地址改为loader(ld.so)的entry point
三、动态链接器 ld.so
1.载入elf所需的动态链接库
这部分会记录在ELF中的DT_NEED中
2.初始并合并GOT(data segment 中的全局偏移表)
3.loader流程,详见《How the heck do we get to main》
四、_start
-
将以下参数传递给libc_start_main
- 环境参数起始地址
- main 地址
- .init 调用main之前的初始化工作
- .fini main结束前的收尾工作
五、_libc_start_main
1.流程
- 执行 .init
- 执行 main
- main结束,执行 .fini
- 执行 exit结束程序
总结:
本文只对执行流程进行了概要分析,后面将从libc、kernel源码进行详细分析
参考地址:
http://www.man7.org/linux/man-pages/man8/ld.so.8.html
http://refspecs.linuxbase.org/LSB_3.1.0/LSB-generic/LSB-generic/baselib---libc-start-main-.html
http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html