应用程序加载的流程
在引入应用程序加载的流程之前,先介绍一些关于库,编译过程的概念
-
库:可执行的二进制文件,可以能够被才做系统加载到内存,分为静态库和动态库,动态和静态的区别是链接的区别,关于静态链接和动态链接可以参照 “程序员的基本修养” 这本书,关于链接,讲的非常好
-
编译的过程
先总的预览下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_initialized
doInitialization
进行初始化,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
来处理.