阅读源码方法汇总

Table of Contents

1. 源码选择

如果只是单纯学习源码阅读,尽量学不需要太多背景的源码来做分析,因为选择需要有背景知识的源码成本就要高点,比如你要分析 Apache 的源码,就得学 Apache。有时候并不需要完整读完整套源码,可能只会分析一小块地方,比如你可能只会分析 Linux 文件系统怎么实现,而不会去读完整个源码。

带有目的去阅读源码效果为佳,比如:

去深入理解项目中用到的库;

解决遇到一些坑;

结合到理论知识学习,比如学习操作系统、算法、RFC 文档等;

学习新的编程模式;

学习别人的编程风格;

满足某些开发需求;

了解工具实现原理;

挖漏洞;

...

2. 让源码能编译并工作起来

源码分析的第一步不是用编辑器硬读代码,最重要的起步就是:先学会用它。

如果是软件,就先学会怎么用(成功编译、使用);如果是库,一般通过以下几个方式就能学会使用:

  • 官方文档
  • 相关书籍。并不是每个库都有清晰的文档,如果文档看不懂,最好能借助相关书籍。
  • 代码仓库里的一些示例代码
  • 单元测试

3. 寻找入口

先从入口开始看,比如 C 语言的 main 函数,如果是 API 的话,就直接看 API 的代码。

有个技巧——运行时打印出函数调用的过程,比如 Python 可通过下面方式查看函数调用流程:

python -m trace –trackcalls [文件名]

不同的语言和库有不同的方式,甚至可以用单步断点等方法。

另外,同时观察:

  • 包含了哪些头文件、导入的哪些库,从而了解文件大概实现原理;
  • 全局变量;
  • 使用方法(Usage)和参数处理。

4. 工具

Source Insight 是一款运行在 Windows 上的商业源码检索工具,由于不跨平台,并且费用昂贵,很长一段时间我都在研究开源的代替方案,测试中发现 OpenGrok 和 GNU GLOBAL 很好用。

4.1. OpenGrok 和 GNU GLOBAL

有一些源码检索工具对于 Linux 内核这类规模的源码检索就存在性能问题,而 OpenGrok 和 GNU GLOBAL 这两款开源的源码检索十分好用,大大小小的项目都适用。

OpenGrok 有一个 Web 界面,把源码索引搭在服务器上用 Web 来看源码非常方便,甚至有时在客厅用 iPad 看源码。详细的请见:https://github.com/OpenGrok/OpenGrok

GNU GLOBAL 生成索引数据,很多编辑器都有支持的插件,如 Emacs 和 Vim。同时它也可以在命令行下使用。官网:http://www.gnu.org/software/global/

GNU GLOBAL 使用举例:

在项目目录下运行 gtags 命令生成索引。

global 命令负责符号检索:

例,要查找 main 函数的位置:global main
例,查找 main 函数在哪些文件中被引用过:global -r main
例,更新索引:global -u,这个命令可以配置在 Emacs 中间隔一段时间就运行一次

4.2. Universal Ctags

一个支持语言多、开发活跃的 ctags 的实现,官网:https://ctags.io/

生成索引:

$ ctags -eR

参数说明:

-e:生成可用于 Emacs 的格式。

-R:递归目录。

Emacs 中操作说明可以参考官方文档:

https://www.gnu.org/software/emacs/manual/html_node/emacs/Looking-Up-Identifiers.html

https://www.gnu.org/software/emacs/manual/html_node/emacs/Identifier-Search.html

4.3. 其他工具

  • 小项目用 Ctags 和 Emacs 自带的 Etags
  • IDE:PyCharm 社区版,IDEA 社区版
  • 思维导图
  • 纸和笔,边看代码边画流程图,这是最顺手也最快捷的方法。

其他还有一些:

  • Doxygen,生成函数调用关系图
  • Argouml,UML绘制工具

运行时的工具:

  • GDB
  • Strace/Dtrace
  • SystemTap

5. 了解架构

好的软件都有好的架构,要学了解常用的软件架构。Project有别于脚本,脚本代码相对来说较随意,Project 更有组织性。

  • 设计模式和 OOP,分析类之间是如何组织的;
  • 自动构建,比如 Makefile,还有 Java 的 Ant 等等,并且分析它们在项目里是如何组织代码的;
  • 模块划分,根据项目文档或者目录组织,了解划分的模块;
  • 编程风格约定;
  • 常见的数据结构,如链表、队列等。

有时候不要纠结在某个函数的算法上,否则浪费很多时间的,清楚这个函数是干嘛的,输入什么,输出什么就 OK 了。后面追究细节的时候再来看这个具体实现也不错。项目太大可以不用看完整体源码,看关注的那部分模块即可。

6. 了解一个函数的功能

  1. 最基本的,从函数名是否能读出函数的功能;
  2. 看函数的头注释;
  3. 是否有文档;
  4. 测试用例;
  5. 看看哪些地方调用了它,以及是如何调用的。

对于代码行数很长的函数,去精简它,删掉多余的判断逻辑、不重要的逻辑路径。

7. 使用调试器

学会了解软件在运行时候的动态,代码是一个样,运行的时候又是另外一个样子,所以要学会用调试器,学会调试软件,在不懂的地方可以下断点,或者在某些地方打印某些东西,看源码也可以边修改边理解。

8. 遇到问题

  1. 记录下来,反复阅读源码,回头再看;
  2. 不要纠结细节,明白这里干嘛的即可,回头再看;
  3. 不懂的地方去 Google,如果遇到语言语法问题,去学习;
  4. 实在不行问别人吧。