应用程序的加载--上

482 阅读3分钟

应用程序加载的流程

在引入应用程序加载的流程之前,先介绍一些关于库,编译过程的概念

  1. 库:可执行的二进制文件,可以能够被才做系统加载到内存,分为静态库和动态库,动态和静态的区别是链接的区别,关于静态链接和动态链接可以参照 “程序员的基本修养” 这本书,关于链接,讲的非常好

  2. 编译的过程 image.png

先总的预览下app启动的过程图 image.png

  • image: 库映射一份到内存,就是加载image
  • 当我们在debug的过程中,可以在LLDB 中输入image list 获取当前app已经加载的库. 在main函数第一行打一个断点,可以看到调用main的调用堆栈的栈底是dyld_start, 是属于dyld这个库里面的. 我们可以到apple open resouce 上下载到最新的dyld的源码,在工程中全局搜索dyld_start image.png 发现就是调用dyldbootstrap::start这个函数, 源码如下图 image.png 入参:
  • dyld3::MachOLoaded* appsMachHeader,这是app Mach-O文件的header, 里面存放了一些load commands, cpu type ...可以参见Mach-O这篇文章去查看Mach-O的结构是什么,或者直接用MachOView这个app去查看Mach-O的结构 image.png
  • dyld3::MachOLoaded* dyldsMachHeaderdyld Mach-Oheader

dyld::main

dyldbootstrap::start的实现我们可以看到,最终返回的是dyld::_main,由于dyld::_main这个函数太长了, 直接将分析的dyld::_main的流程附上

image.png

  1. 准备条件,环境,平台,路径,主机信息之类的
  2. 共享缓存加载mapSharedCache(mainExecutableSlide)
  3. 实例化主程序mainExecutable = instantiateFromLoedImage
  4. 加载插入的动态库loadInsertedDylib
  5. link主程序
  6. link插入的动态库
  7. initializeMainExecutable初始化主程序
  8. 主程序初始化完毕,调用main 注:ImageLoader是一个类,负责将image加载进内存,且每一个image对应一个ImageLoader实例来负责加载, 并且是递归加载的。

initializeMainExecutable里面又做了什么呢?

  1. for循环遍历sImageRoots, sImageRootsImageLoader的实例的数组,每个ImageLoader开始runInitializers.
  2. 当执行完1,主程序开始runInitializers 那么在sImageRoots里的每个ImageLoader runInitializers的时候,这个runInitializers做了什么呢?

调用 processInitializers, processInitializers里面每个image调用recursiveInitialization, 进行递归初始化. recursiveInitialization 分为下面三步:

  1. context.notifySingle 通知将要初始化dyld_image_state_dependents_initialized
  2. doInitialization进行初始化,doInitialization里面分为两步:1. doImageInit, image 进行初始化; 2. doModInitFunctions, 根据对doModInitFunctions的代码分析可以知道doModInitFunctions其实就是调用libsystemlibSystem_initializer. 而且在doImageInit的时候可以知道,如果libSystem没有被initialized,是不允许对别的imageinitialize的处理的。 那么由此可以知道libSystem是第一个被加载初始化的库的.

那么为什么libSystem是需要第一个被加载初始化的呢?由于libSystem是若干个系统lib的集合包含libdispatch, libsystem_c, libsystem_blocks,所以它只是一个容器lib而已,而且它也是开源的,里面实质上就一个文件,init.c, 由 libSystem_initializer调用了libdispatch libdispatch_init, libdispatch_init再调用libobjc_objc_init,这就是objcruntime的初始化入口.

在上面第一点context.notifySingle中的notifySingle中会调用一个函数指针NotifyObjCInit就是在_objc_init中赋值的. notifySingle是通知image加载的各种状态, 源码如下图 image.png NotifyObjCInit指向的就是libobjc中的load_images.

可见dyld担当了runtimeImageLoader中间人,当新的image加载进来之后, 由runtime去解析二进制文件的符号表和代码, 加载class之类的.(runtime就是libobjc)

总结

  1. dyld 将主程序iamge初始化
  2. ImageLoader读取image
  3. 先将libsystem初始化,并且由调用libSystem_initializer来调用runtime_objc_init, 注册回调. 当image被加载到内存后,通知runtime来处理.