总体上是这几点:

  1. 以祈祷、读文档、读注释、看issue为理解代码的主要手段
  2. 以读代码、debug为辅助理解代码的方法
  3. 以debug为验证理解的主要手段
  4. 每日整理当日理解的内容
  5. 不理解的留个印象,先跳过

为什么要祈祷?

因为作者不一定太关心文档和注释:大多数人以正确实现为主要目标。所以要祈祷文档和注释是正确的,或者说大多数是正确的。

为什么不考虑没有文档和注释的情况?理由很简单,如果该项目不出名,作者也懒得维护,所以这些东西没有或不完善是很正常的。

So,挑著名一点的吧。

为什么要读文档?

如果文档的质量足够高,那么文档中会记录代码每个部分,至少是关键部分的概览,并且还会关联到代码中的变量名。

如redis的backlog,实际上你可以直接通过grep在代码中找到对应的变量,到此就可以直接推测哪些函数在操作backlog,这样可以帮助你理解代码。

为什么要读issue?

issue是用户对作者提需求,所以它至少可以解释为什么有这个功能。

但是能够提issue的人都应该是程序员,所以不论是作者还是用户都会讨论这个功能或bug的原因、设计细节。

比如redis用的epoll,是epoll_create而不是epoll_create1,原因就是兼容旧版本内核。

image

为什么要整理?

理由很简单,代码是复杂的,而人容易忘事情。

且不说读代码,就算是写代码。你今天写了一个函数,两周后你看到了,可能会在想“这是谁写的shit”。

不要相信手中的草稿纸会记录清楚这一切,因为首先纸笔写下的内容不方便修改,你可能要把一大堆东西写到一片很小的空间里,其次草稿纸就是帮助你理解一小段东西的,所以会很乱,所以第二天来了,就再也不想看了。

我曾经就很喜欢用草稿纸,结果是虽然写了很多,但是第二天几乎还是从头来。看看我用了多少草稿纸吧,里面的笔是一个参考物。

image

如何整理?首先利用电子技术,画一遍执行流程图,流程图旁边至少标记这是哪个函数做的事情,甚至你可以精确到哪一行。然后记下你今天研究的问题是什么,下一个要研究的问题是什么。如果有网页,记得也贴上。

如果还用上了断点,记得也保存一份断点。

对于不理解的内容

因为读代码的时候,对整个项目的理解度不足,所以出现了一些难以理解的部分,可以考虑跳过。

怎么对不理解的内容留个印象?

首先,如果可以的话,看注释,或者利用关键词查文档,大概了解一下这是干嘛的。比如redis在复用rdb的时候,注释里说这是复制缓冲区。但是你不知道这个缓冲区是什么,也不知道其作用。查看了一下,操作了backlog,得知这个是复制命令的历史记录的位置。

            /* Perfect, the server is already registering differences for
             * another slave. Set the right state, and copy the buffer.
             * We don't copy buffer if clients don't want. */
            if (!(c->flags & CLIENT_REPL_RDBONLY))
                copyReplicaOutputBuffer(c,slave);

但是还是不理解怎么办?没办法,你只能在整理的时候,在哪个文件的哪些地方有不理解的地方。

理解基础部分

读代码的时候,你至少要明白整个程序最基础的部分,比如线程模型。然后还要找到一些明显很通用的东西,比如TCP连接的封装。如果你不读线程模型,那么你就不知道一个流程走完了,其接下来的步骤。

比如一个PING/PONG,收到了PING,然后设置写入回调来发送PONG。如果不理解线程模型,这个很可能就被忽略了。

当然还有一些东西要注意的,比如定时器,一定要知道作者的设计。我个人不建议redis的定时器的实现,因为它要做太多事情了!一个serverCron里面做的事情非常多,既要管cluster,还要管主从复制,还要更新自己的状态,还要负责重连!

当然我暂时没想明白什么样的定时器能够同时做到编写时容易,且读代码的时候也容易的。