上一篇文章咱们讲到了启动优化须要的一些理论知识,这篇文章咱们讲一下从exec()到main()系统帮咱们作了哪些操做。缓存
exec()函数是一个系统调用,当启动一个应用程序的时候,系统内核把应用映射到新的地址空间,且每次起始位置都是随机的(由于使用了ASLR),并将起始位置到0x000000这段范围的进程权限都标记为不可读写不可执行。安全
首先,内核加载动态连接库的帮助程序Dyld,让Dyld来启动应用的进程。Dyld的工做是:bash
应用所依赖的dylib文件可能会依赖其它的dylib,因此dyld加载dylib的过程是一个递归的调用过程。ide
在加载完全部依赖的dylib后,它们是彼此独立的,咱们须要将它们绑在到一块儿,这个过程就是修复。由于代码签名的存在,咱们没法修复指令,那么就不能让一个dylib调用另外一个dylib,这时须要加载更多的中间层。函数
现代的code-gen被称为动态PIC(位置无关代码),能够加载到该地址上,而且是动态的,也就是说地址被间接的分配了。当调用发生时,code_gen会在__DATA段中建立一个指向被调用者的指针,而后加载该指针并跳转过去。优化
因此dyld作的事情就是修正(fix-up)指针和数据,Fix-up有两种类型,rebasing(重设地址)和binding(绑定)。spa
Rebasing指的是在镜像内调整指针,Binding指的是在镜像外调整指针。指针
能够在任何二进制文件上使用dyldinfo指令来查看全部的修复:code
在过去,dyld会把dylib加载到指定的地址,全部指针和数据对于代码来讲都是对的,dyld无需作任何fix-up。现在使用了ASLR会将dylib加载到新的随机地址,这个随机地址跟代码和数据指向的旧地址会有误差,dyld须要修正这个误差(slide),Rebasing就是将dylib内部的指针地址都加上这个偏移量,计算方法以下:cdn
Slide = actual_address - preferred_address
复制代码
当咱们重设地址时,实际上在全部的Data页面上都产生了错误,而后对页面进行修改,就会产生COW(写入时复制),因此重设地址有时会很是昂贵。这可能会产生I/O瓶颈,但由于rebase的顺序是按地址排列的,因此从内核的角度来看这是个有次序的任务,它会进行预读,减小I/O消耗。
下期预告:iOS启动优化实践篇
关注公众号,获取更多文章内容