首页 > 代码库 > 程序的启动过程

程序的启动过程

WIN32程序启动过程

第一步:操作系统首先创建响应的进程并分配私有的进程空间,然后操作系统的加载器负责把可执行文件的数据段和代码段映射到进程的虚拟内容空间中。

第二步:加载器读入可执行程序的导入符号表,根据这些符号表可以查找出该可执行程序所有依赖的动态链接库。

第三步:加载器针对该程序依赖的每一个动态链接库调用LoadLibrary。

  • 查找对应的动态库文件,加载器为该动态链接库确定一个合适的基地址。如果该基地址和动态链接库希望加载的基地址不同,加载器还要为该库做rebase,然后把整个动态链接库映射到进程的虚拟内存空间中。
  • 加载器读取该动态链接库的导入符号表和导出符号表,比较应用程序要求的导入符号是否匹配该库的导出符号。
  • 针对该库的导入符号表,查找对应依赖的动态链接库,如果有则跳转第三步。
  • 调用该动态链接库的初始化函数。
  • 知道所有应用程序直接/间接依赖的动态链接库处理完毕,全部映射到应用程序进程空间,成为应用程序进程的一部分。

第四步:初始化应用程序的全局变量,对于全局对象自动调用构造函数。

第五步:进入应用程序入口点函数开始执行。

Linux程序启动过程

第一步:用户在Linux命令终端输入运行程序的命令,然后按回车键。

第二步:首先接管的是exec系统调用,它会为应用程序的运行准备一些环境变量等,并且为运行的命令找到相应的解释器。

第三步:通常应用程序的解释器就是ld(loader/加载器),ld接管控制器权后首先需要读入这个可执行程序的文件的一部分,包括文件头及共享对象(so,对应于Windows下的动态链接库)区等。然后检查这个可执行文件所依赖的共享对象so(这些信息都在可执行文件中),并且在LD_LIBRARY_PATH和系统默认库文件夹的位置查找这些库是否存在。如果不存在,则报告错误并退出执行。

第四步:针对每一个依赖库,ld需要首先读入这个so的一部分文件头和相关信息。然后递归查找该共享对象所以来的其他共享对象,直到最底层。在这个过程中,ld会在内部维护一个数据结构用来记录所有这些共享对象之间的相互依赖关系。最终,ld会确认所有的该可执行程序直接或间接依赖的so都存在。

第五步:ld会把所有依赖的so映射到该程序的进程空间的虚拟内存中(知识映射,并不是把全部so文件的内容读入内存)。显然,每一个共享对象在该进程的虚拟内存空间中占据不通用的连续区域,它们的基地址各不相同,从而其内部的一些用绝对地址表示的符号需要做出相应的修改。这个过程成为relocation过程。

第六步:初始化应用程序的全局变量,对于全局对象自动调用构造函数。

第七步:进入main函数开始执行。

 

摘自C++应用程序性能优化

程序的启动过程