应用程序加载的流程
在引入应用程序加载的流程之前,先介绍一些关于库,编译过程的概念
-
库:可执行的二进制文件,可以能够被才做系统加载到内存,分为静态库和动态库,动态和静态的区别是链接的区别,关于静态链接和动态链接可以参照 “程序员的基本修养” 这本书,关于链接,讲的非常好
-
编译的过程
先总的预览下app启动的过程图
- image: 库映射一份到内存,就是加载image
- 当我们在debug的过程中,可以在LLDB 中输入image list 获取当前app已经加载的库.
在
main函数第一行打一个断点,可以看到调用main的调用堆栈的栈底是dyld_start, 是属于dyld这个库里面的. 我们可以到apple open resouce 上下载到最新的dyld的源码,在工程中全局搜索dyld_start发现就是调用
dyldbootstrap::start这个函数, 源码如下图入参:
dyld3::MachOLoaded* appsMachHeader,这是app Mach-O文件的header, 里面存放了一些load commands, cpu type ...可以参见Mach-O这篇文章去查看Mach-O的结构是什么,或者直接用MachOView这个app去查看Mach-O的结构dyld3::MachOLoaded* dyldsMachHeader,dyld Mach-O的header
dyld::main
从dyldbootstrap::start的实现我们可以看到,最终返回的是dyld::_main,由于dyld::_main这个函数太长了, 直接将分析的dyld::_main的流程附上
- 准备条件,环境,平台,路径,主机信息之类的
- 共享缓存加载
mapSharedCache(mainExecutableSlide) - 实例化主程序
mainExecutable = instantiateFromLoedImage - 加载插入的动态库
loadInsertedDylib - link主程序
- link插入的动态库
initializeMainExecutable初始化主程序- 主程序初始化完毕,调用
main注:ImageLoader是一个类,负责将image加载进内存,且每一个image对应一个ImageLoader实例来负责加载, 并且是递归加载的。
在initializeMainExecutable里面又做了什么呢?
for循环遍历sImageRoots,sImageRoots是ImageLoader的实例的数组,每个ImageLoader开始runInitializers.- 当执行完
1,主程序开始runInitializers那么在sImageRoots里的每个ImageLoader runInitializers的时候,这个runInitializers做了什么呢?
调用 processInitializers, processInitializers里面每个image调用recursiveInitialization, 进行递归初始化. recursiveInitialization 分为下面三步:
context.notifySingle通知将要初始化dyld_image_state_dependents_initializeddoInitialization进行初始化,doInitialization里面分为两步:1.doImageInit,image进行初始化; 2.doModInitFunctions, 根据对doModInitFunctions的代码分析可以知道doModInitFunctions其实就是调用libsystem的libSystem_initializer. 而且在doImageInit的时候可以知道,如果libSystem没有被initialized,是不允许对别的image做initialize的处理的。 那么由此可以知道libSystem是第一个被加载初始化的库的.
那么为什么libSystem是需要第一个被加载初始化的呢?由于libSystem是若干个系统lib的集合包含libdispatch, libsystem_c, libsystem_blocks,所以它只是一个容器lib而已,而且它也是开源的,里面实质上就一个文件,init.c, 由 libSystem_initializer调用了libdispatch 的 libdispatch_init, libdispatch_init再调用libobjc的_objc_init,这就是objc和runtime的初始化入口.
在上面第一点context.notifySingle中的notifySingle中会调用一个函数指针NotifyObjCInit就是在_objc_init中赋值的. notifySingle是通知image加载的各种状态, 源码如下图
NotifyObjCInit指向的就是libobjc中的load_images.
可见dyld担当了runtime和ImageLoader中间人,当新的image加载进来之后, 由runtime去解析二进制文件的符号表和代码, 加载class之类的.(runtime就是libobjc)
总结
- dyld 将主程序
iamge初始化 - 由
ImageLoader读取image - 先将
libsystem初始化,并且由调用libSystem_initializer来调用runtime的_objc_init, 注册回调. 当image被加载到内存后,通知runtime来处理.