点击 Run 之后发生了什么?
来源:互联网 发布:象棋算法 编辑:程序博客网 时间:2024/04/28 00:53
来源:伯乐在线 - Martin_wjl
点击 → 申请加入伯乐在线专栏作者
如需转载,发送「转载」二字查看说明
这是我以前去链家网面试的一个题目,当时回答不够全面,现在看了2016 WWDC以及Sunnyxx iDev大会的分享之后,感觉对这个问题有了一些简单的认识,就来总结下。如果大家有补充,麻烦评论下哦!
简单来说,点击 Run 之后 App 进行编译、汇编、链接、代码签名以及启动执行等操作
编译
编译主要依靠编译器来完成一系列的操作,主要的操作有预处理、词法分析、语法分析、语义分析、生成中间代码、生成目标代码以及目标代码优化
OS X 现在使用的编译器是 LLVM (Low Level Virtual Machine),在最初使用的是 GCC作为官方的编译器,但是由于下面的众多限制,apple 使用了自己的一套编译器
GCC 原名为GNU C语言编译器,它原本只能处理 C语言,后来扩展了Objective-C、Java等语言,但是对于Objective-C的处理还是存在众多不便
GCC 效率低下、性能不强
苹果开发自己的编译器,有利于开展自己的工具链,比如后期做 Swift、lldb 等
借用 sunnyxx的图片 来展现下 llvm 的基本架构
编译器分别编译器前端(clang)和编译器后端,编译器前端负责产生机器无关的中间代码,编译器后端负责对中间代码进行优化并转化为目标机器代码,对于为什么需要 中间代码这个东西,看个图就一目了然啦(IR:intermediate representation中间表示)
预编译
预编译主要用来处理那些源文件中以 #开头的预编译命令,比如#include等
词法分析
将输入分解为一个个独立的词法符号,也叫单词符号(token)
// find a zero
float matchZero(char *s){
}
上面的小程序,返回下列单词流
FLOAT、ID(MATCHZERO)、LPAREN、CHAR、ID(S)、RPAREN // 大概了解即可,觉得背这些词法符号定义对目前 iOS 开发用处不大
注释、宏、空格、换行等都不是单词
语法分析
将符号化的字符串,转化抽象为可以被计算机存储的树形结构,即抽象语法树(AST),并验证语法的正确性,例如:忘记带分号
语义分析
语法分析只能完成语法层面的分析,无法对整个语句的真正意义进行判别,比如,讲一个浮点数赋值指针类型的时候,语义分析器就会发现类型不匹配,编译器提出相应的错误警告。
语义分析主要做的事情就是类型检查、以及符号表管理
生成中间代码
编译器前端负责产生机器无关的中间代码,编译器后端负责对中间代码进行优化并转化为目标机器代码
目标代码的生成与优化
编译器后端主要包括代码生成器、代码优化器。代码生成器将中间代码转换为目标代码,代码优化器主要是进行一些优化,比如删除多余指令,选择合适寻址方式等
汇编
目标代码需要经过汇编器处理,才能变成机器上可以执行的指令。生成对应的.o文件
链接
链接器(这里指的是静态链接器)将多个目标文件合并为一个可执行文件,在 OS X 和 iOS中的可执行文件是 Mach-O,对于Mach-O的文件格式可以参考这里,刚才所描述的过程其实可以用 sunnyxx的一页 ppt来进行总结
链接呢,又分为静态链接和动态链接
静态链接
静态链接:在编译链接期间发挥作用,把目标文件和静态库一起链接形成可执行文件
动态链接
动态链接:链接过程推迟到运行时再进行。对于动态链接和静态链接,各有千秋
如果多个程序都用到了一个库,那么每个程序都要将其链接到可执行文件中,非常冗余,动态链接的话,多个程序可以共享同一段代码,不需要在磁盘上存多份拷贝,但是动态链接发生在启动或运行时,增加了启动时间,造成一些性能的影响。
静态库不方便升级,必须重新编译,动态库的升级更加方便
代码签名
我们每次build之后,都会发现工程目录下多了一个.app文件
在 .app目录中,有又一个叫_CodeSignature的子目录,这是一个 plist文件,里面包含了程序的代码签名,你的程序一旦签名,就没有办法更改其中的任何东西,包括资源文件,可执行文件等,iOS系统会检查这个签名。
签名过程本身是由命令行工具 codesign 来完成的。如果你在 Xcode中build一个应用,这个应用构建完成之后会自动调用codesign 命令进行签名,这也是Link之后的一个关键步骤。
启动
在经过一系列处理后,终于形成一个可以在系统上跑起来的可执行程序,剩下的就是启动运行
其实在启动过程中,dyld(动态链接器) 起了很重要的作用,进行动态链接,进行符号和地址的一个绑定
dyld 主要在启动过程中主要做了以下事情:
加载所依赖的dylibs
Fix-ups:Rebase修正地址偏移,因为 OS X和 iOS 搞了一个叫 ASLR的东西来做地址偏移(随机化)来避免收到攻击
Fix-ups:Binding确定 Non-Lazy Pointer地址,进行符号地址绑定。
ObjC runtime初始化:加载所有类
Initializers:执行load 方法和__attribute__((constructor))修饰的函数
对于如何减少启动时间,今年的 WWDC 也有详细的阐述,可以在参考链接中找到相关的资料。
总结
那么总体的过程可以概括为:编译 – 汇编 – 静态链接 – 签名 – 启动(动态链接等操作)- 跑起来了
继续借用 Sunnyxx 的 ppt
参考链接
虎书
程序员的自我修养
MDCC 2016 – 孙源(Sunnyxx)- clang 概述
iDev 2016 – 孙源(Sunnyxx)- 把玩链接器
优化 App 的启动时间
WWDC 2016 Session 406
- 点击 Run 之后发生了什么?
- new之后发生了什么?
- 从launcher中点击一个应用icon之后发生了什么?
- malloc()之后,内核发生了什么?
- 实例化子类之后发生了什么
- malloc()之后,内核发生了什么?
- malloc()之后,内核发生了什么?
- malloc()之后,内核发生了什么?
- 输入URL之后都发生了什么
- 插入usb之后发生了什么
- 输入网址之后发生了什么
- url enter之后发生了什么
- 点击打印按钮时发生了什么?
- Android点击应用Icon发生了什么
- C++中new和delete之后发生了什么
- 前端面试:页面输入url之后发生了什么?
- 用户点击Log In按钮后发生了什么?
- Windows中点击“关闭”按钮发生了什么?
- 第三方微博登录:你所访问的站点在微博认证失败,错误号:21322
- 1057. 数零壹(20)
- PrefixSpan算法详解
- Hibernate基础:快速入门(5):删除数据
- MATLAB的GUI入门
- 点击 Run 之后发生了什么?
- JSP九大内置对象
- Hadoop编程 在HDFS里新建文件并写入内容,以及输出
- 使用java反射技术完成对象所有属性值的输出
- 数据结构----学期总结
- System Level I/O && Network Programming
- 昨天写了一个让屏幕保持长亮的app
- 【C#】51. Await 处理并行任务(WhenAll)以及Task.Delay()
- 布局万花筒:UIColletionview