前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何持续的自我提升

如何持续的自我提升

作者头像
酷酷的哀殿
发布2021-02-01 11:12:55
7260
发布2021-02-01 11:12:55
举报
文章被收录于专栏:酷酷的哀殿

最近经常遇到有些朋友问题我如何学习编译器等各类底层的知识。

这些问题的背后是很多程序员对自我提升的强烈需求。

今天,笔者会通过分享“2020年最后一天的学习完整记录”的方式解答这个问题。

注意:本文是分享自我提升技巧,所以遇到不懂的技术名词,可以直接跳过。

一、初识

初识是一个被灌输知识的过程。

当我们看博客或者书籍时,都会遇到一些新知识。这就是初识

今天,笔者从 百度App Objective-C/Swift 组件化混编之路(二)- 工程化 时,就被灌输了一个”新知识“:module 会供链接器使用

下面截取部分原文:

“1.2 Module 化 1.2.1 基本概念

  • module:是一个编译单元,或构建产物,对一个软件库的结构化替代封装,供链接器使用(更多介绍请查阅 Clang-Module:https://clang.llvm.org/docs/Modules.html#introduction)

二、思考

思考 是一个主动消化知识的过程。

思考 的方式有很多:

  • 新知识是否和已有的知识发生了冲突?
  • 新知识可以和哪些知识串联起来?
  • ....

当我看到上面的”新知识“时,就会想:

  • ”module 是如何被链接器使用呢?“
  • ”我也看过很多相关资料,为什么之前看到的资料都没有提到链接器呢?“

三、探索

探索 是一个手动进行研究的过程。

探索 过程非常依赖我们的思考能力和记忆能力。

下面是笔者对 module 的一些思考:

  • 我具备多少与 module 相关的知识
  • 可以通过哪些搜索引擎技巧更快的搜索到与 module 相关的知识
  • 我们是否有方案验证 module 与 链接器 的关系
  • ...

每一次思考都需要我们把记忆能力充分调用:

  • 回忆与 module 相关的知识
  • 回忆搜索引擎技巧
  • 回忆如何通过 Xcode 创建工程、动态库
  • 回忆 APP 构建的每一步的命令
  • ...

通过上面的初步思考,我决定通过创建 Demo 的方式对 module 会供链接器使用 进行验证。

准备 Demo工程

image-15402758

Demo 工程会有一个名为 Host 的 APP,同时该 App 会依赖名为 FrameW 的动态库和其它系统库。

项目的整体架构如下图:

构建

通过 xcodebuild 命令,可以对 Host 进行构建。

如下,红框部分是 Xcode 执行 链接 Host 时,所调用的命令:

image-20521724

通过仔细分析上面的完整 命令 信息,我们没有发现与 module 明显相关的参数,所以,我们可以大胆猜测module 与链接器没有关系

调试模式

考虑到编译器可能通过其它方式进行了信息传递,所以,我们通过给上述命令添加参数 -v 的方式进行调试。

image-20917926

很遗憾,新增参数 -v 后,仍然没有得到有效的信息。但是,我们得到了一个新的知识 clang 会调用 ld 命令执行链接任务。

调试链接过程

接着,我们再次尝试对 ld 命令添加参数 -v -t 的方式进行调试。

这次的信息量十足:

  • 链接依赖的 .o 路径被完整的打印出来了
  • 链接依赖的 FrameW 路径被打印出来了
  • 链接依赖的系统库路径被打印出来了

image-21201007

module 文件探究

现在局面很清晰了,ldFrameW 的交互是通过上面日志中的 FrameW.framework/FrameW 文件完成的。如果我们能够证明 FrameW.framework/FrameW 文件与 module 没有关系,就可以证明 ”module 与链接器没有关系“。

下面,我们将依次分析 module 文件与 FrameW.framework/FrameW 文件,并将两者对比。

“如果对某些细节比较好奇,可以在公众号底部留言,我们后续再分享 module 内幕相关的知识。

module 文件特征

首先,我们进行编译任务时,会发现有一个名为 -fmodules-cache-path 的参数,该参数的值是一个路径ModuleCache

代码语言:javascript
复制
 -fmodules-cache-path=/var/folders/4j/jqzrrjzn0nvgm4pyxrqddxnmm530jm/C/org.llvm.clang.xxxx/ModuleCache
  • 通过 cd 命令切到该文件夹
  • 通过 find 命令配合 grep 找到 FrameW-xxx.pcm
  • 通过 file 命令,得知该文件是 Mach-O 文件
  • 通过 otool -l grep sort,我们发现该文件最大的一个 section 体积是 0x000000000000d318
  • 通过 0x000000000000d318 配合 otool -lgrep -B 3 ,我们知道它属于 __CLANG __clangast 部分
  • 通过 segedit 命令将该部分导出到单独文件
  • 通过 file ,我们了解到该文件属于 data
  • 通过 xxdhead 命令,我们可以得到 magic numberCPCH

image-24230541

  • 通过 CPCHllvm 源码,我们可以判断这是 AST/PCH file magic number

image-30138062

另外,结合 llvm 源码的 llvm::Error GlobalModuleIndexBuilder::loadModuleFile 内部逻辑,我们可以确认这个就是 动态库module 进行编译后的产物

image-30220599

通常上面的思考,我们可以得到以下结论:module 通常会被编译为单独的 mach-o 文件,该文件主要负责在 section:__CLANG __clangast 存储编译后的 ast 文件。

动态库的特征

ld 链接的动态库 FrameW.framework/FrameWMach-O 64-bit dynamically linked shared library arm64 文件

通过上面两种文件的特征,我们可以证明 module 与链接器没有关系

“考虑到很多情况,我们没法找到各种命令行工具进行分析,所以,下面介绍一份搜索引擎版本的探索流程

  • 通过搜索引擎查找 “Xcode 教程”
  • 通过 Xcode 教程了解构建的完整步骤,并观察其中的链接环节
  • 通过 Xcode 的链接环节,我们可以发现真正执行链接的二进制文件是 ld
  • 通过搜索引擎查找“如何通过二进制文件找到对应的源码”
  • 尝试编译链接器(遇到问题,可以通过网络引擎搜索解决)
  • 运行链接器,并分析链接器的源码执行路径

注意:

  1. Xcode 的构建流程,笔者在上次分享的 llvm 编译器高级用法:第三方库插桩 有过简单介绍,后面会有更加详细的文章分享动态库、静态库、APP 的构建流程。
  2. 通过二进制文件获取源码,笔者在上次分享的 iOS 崩溃排查技巧:如何获取系统库源码,后面会有一篇升级版本的获取系统库源码的方案。

四、总结和超越

总结 是对整个学习成果的强化过程。

通过 ”初识“-”思考“-”探索” 三部曲,我们会学到很多知识。

不幸的是,人类的大脑很容易遗忘知识。所以,我们需要一些技巧将记忆强化。

总结 的方法有很多种,其中最高效的方案就是 费曼技巧

费曼技巧 是一种「以教为学」的学习方式,通过直白浅显的语言把复杂深奥的问题和知识传授给 小孩子 的方式进行学习。

但是,费曼技巧 需要一个倾听者才会有很好的学习效果。所以,我个人更加推荐将 学习笔记公开

当我们选择将笔记发到公众号或者个人博客时,就会强迫自己将整篇文章的知识理顺,避免错误、遗漏。同时,也会有更多的同行帮我们 指出问题

请记住,只有当我们把相关的知识点完整串联和记忆后,我们才能真正 超越自己,才能真正的 自我提升

本文总结

本文通过一次完整的学习经历分享了 “如何自我学习/提升”的问题。

整体的思路如下:

  • 初识:被灌输知识的过程。
  • 思考:主动消化知识的过程。
  • 探索:手动进行研究的过程
  • 总结和超越:完成自我提升的过程

后记

除了本文分享的知识外, 百度App Objective-C/Swift 组件化混编之路(二)- 工程化 还有很多的知识或者疑惑点值得研究,下面简单的列举几个,欢迎读者进行补充。

  • .tbd 是文本类型,为什么原作者会认为 dynamic_library 的扩展名是 .tbd
    • dynamic_library:动态库,Xcode 7 之前扩展名为 .dylib, Xcode 7 后是 .tbd ;目前官方环境并不允许为 iOS 平台添加这种类型。
  • swiftmodule 的依赖会传递吗?有没有优雅的方式解决? “4.7 小知识:swiftmodule 的传递依赖性 已知:有组件 A 依赖组件 B,组件 B 依赖组件 C 在 Objective-C 中,B 对外暴露的头文件中引用了 C 的公开头文件,我们叫组件 B 传递依赖 C,结果就是编译组件 A 时必须同时能找到组件 B 和组件 C 的头文件,否则编译失败。 然而 Swift 并没有公开头文件一说,只要组件 B import C,导致 swiftmodule 中也明确标记了 import C,当组件 A import B 时,也同时 import C ,如果组件 A 找不到组件 C 的 module,那组件 A 将编译失败。
  • 下面解决报错的方案是依据什么原理?在不依赖 Xcode 的情况下,我们该如何解决? “6.3 App 链接一个 Swift 二进制时报错? 当一个组件或产物需要链接其他 Swift 的产物时,比如 App、单测、动态库等,需要告诉 Xcode 开启 Swift 链接功能,开启方法就是添加一个 Swift 文件,否则报错。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 酷酷的哀殿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、初识
  • 二、思考
  • 三、探索
    • 准备 Demo工程
      • 构建
        • 调试模式
          • 调试链接过程
            • module 文件探究
              • module 文件特征
            • 动态库的特征
            • 四、总结和超越
            • 本文总结
            • 后记
            相关产品与服务
            命令行工具
            腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档