main函数执行之前的故事

2019-02-20  本文已影响0人  小鬼快跑

main函数执行之前发生了什么?

操作系统在启动一个程序的时候, 内核会为程序创建一个进程空间,并且会为进程创建一个主线程.主线程会执行各种初始化操作,完成后才开始执行我们在程序中定义的main函数.也就是说main函数并不是主线程最开始执行的函数.在main函数之前其实还发生了很多的事情.
从exec()函数开始到main函数执行.中间经历了众多的步骤.我们 一一来探究一下.
1.把App对应的可执行文件加载到虚拟内存.
2.把dyld库加载到内存.
3.dyld进行动态链接.

加载过程

操作系统内核为可执行程序创建进程空间后,会分别将可执行程序文件以及可执行程序所依赖的动态库文件中的内容加载到进程的虚拟内存地址空间,可执行程序以及动态库文件中的内容是符合苹果操作系统ABI规则的mach-o格式的二进制数据,我们必须要将这些数据加载到内存中,对应的代码才能被执行以及变量才能被访问.我们称每个映射到内存空间中的可执行文件以及动态库文件的副本为image(镜像).注意 此时只是将文件加载到内存中,并没有执行任何用户进程的代码.也没有调用库中的任意初始化函数.当所有的image加载完毕后,内核会为进程创建一个主线程.并将可执行程序的image在内存中的地址作为参数压入用户态的堆栈中,把dyld库中的_dyld_start函数作为主线程执行的入口函数.这时候内核将控制权交给用户,系统由内核态转换为用户态.dyld库来实现进城在用户态下的可执行文件以及所有动态库的加载和初始化逻辑.可见一个程序运行时可执行文件以及所有依赖的动态库其实是经历了两次加载过程:核心态下的image加载.以及用户态下的二次加载和初始化操作,dyld库接管进程后,进程的主线程将从_dyld_start出开始所有用户态下代码的执行.

阶段 工作
加载动态库 dyld动态库从主执行文件中的header获取到需要加载的所依赖动态库列表,然后他需要找到每个dylib.而应用所以来的dylib文件可能是依赖于其他dylib的.所有所需要加载的是动态库列表一个递归依赖的集合
Rebase和Bind - Rebase在Image(镜像)内部调整指针的指向,在过去,会把动态库加载到指定地址,所有指针和数据对于对于代码都是对的,而现在地址空间布局是随机化,所有需要在原来的地址根据随机的偏移量进行计算而做一下调整 - Bind是把指针正确的指向Image外部的内容.这些指向外面的指针被符号(Symbol)名称绑定,dyld需要去符号表里查找,找到symbol对应的实现
ObjcSetup 1.注册Ojbc类 2.把category的方法插入到方法列表 3.保证每一个selector唯一
Initializers 1.Objc的+load()函数 2.C++的构造函数属性函数 3.非基本类型的C++静态全局变量的创建(通常是类或者是结构体)

最后dyld会调用main()函数.

上一篇 下一篇

猜你喜欢

热点阅读