Linux学习系列十九:如何高效的阅读Linux源码

1.引言

如何阅读代码还要单独写一篇文章?难道不是随便用一个IDE就可以了吗?回到上一篇文章里介绍的那个问题,需要修改uboot里board_mmc_init函数里的writel(0x66666666,REG_MFP_GPD_L) ,对于初学者如何在uboot代码里找到这句话呢?当时问我这个问题的网友就有这个困惑。

因为Uboot和Kernel里有非常多数量的文件,另外为了支持多种芯片,在整个目录里存在大量的同名文件、同名函数。所以如果用一般的IDE把整个工程目录加载进去,然后阅读代码,会相当的不方便,你很难理清楚各个函数之间的调用关系。我曾经尝试过在Windows下用SourceInsight去看内核源码,实在看不下去,而且由于文件太多经常卡住。在网上也看到有人通过一些脚本去精简文件数量再配合SourceInsight的,我也尝试过,感觉也不是很好用。见到过几个高手是直接通过VIM阅读,效率很高,我经过几天的摸索,稍微入了一点门,在这里给初学者做个分享,希望对大家有所帮助。               

2.工具安装与使用

因为我一开始学的单片机,用Keil软件比较多,咱们就在Ubuntu里构建一个类似于Keil软件常见功能的环境。为了让大家看起来更直观,我找了一个单片机的工程同时放到Ubuntu里和Window下,两边同时对比分析。

我们需要在Ubuntu系统里安装配置以下几个工具。

2.1 ctags

2.1.1 ctags安装配置

以下一段话摘自于维基百科:

Ctags is a programming tool that generates an index (or tag) file of names found in source and header files of various programming languages. Depending on the language, functions, variables, class members, macros and so on may be indexed. These tags allow definitions to be quickly and easily located by a text editor, a code search engine, or other utility.

安装方式如下:

sudo apt-get install ctags

验证是否安装成功的方式可以输入

ctags --version

使用

ctags --list-languages

可以查看ctags支持的编程语言

使用

ctags --list-maps

可以查看ctags支持的编程语言对应的文件扩展名

使用

ctags --list-kinds

可以查看ctags识别的语法元素,使用

ctags --list-kinds=c

可单查看C语言识别的语法元素

安装完成之后,想要使用ctags,必须在你想要查看的代码目录中有tags文件。

2.1.2 ctags使用

在执行下述操作前,已经在Ubuntu里~/mcuproject目录下放了一个MCU的工程。

第1步: ctags -R *  

生成tags文件。

第2步:需要找到main函数定义在哪里,

先输入vim打开vim窗口,然后在vim命令行窗口输入ts main

它的作用是:Search for a particular tag

再根据提示输入1 回车就跳转到main函数所在的位置了,

有一点需要大家注意的是:一定要在tags所在的目录打开vim,输入ts才能搜到你要找到的tag,在其它目录是不行的,比如我进入到上一级目录,就会提示如下信息了。

对比下Windows里Keil环境下,我通常用如下方式去查找:

搜索结果如下:

看到这里你是不是有个疑问,为什么Ubuntu下搜索main只有一个地方,但是在Keil下搜索出来了6处。

原因是使用ctags搜索的结果是main的定义,而Keil里是只要main这个字符串出现的地方都会被搜索出来。

除了刚才使用的ts命令,还有其他相关命令:

:ts or :tselect  List all of the definitions of the last tag

:tn or :tnext   Go to the next definition for the last tag

:tp or :tprev   Go to the previous definition for the last tag

:tf or :tfirst   Go to the first definition for the last tag

:tl or :tlast    Go to the last definition for the last tag

第3步:ctags两个常用的快捷键

Ctrl-]   Jump to the tag underneath the cursor

Ctrl-t   Jump back up in the tag stack

通过ctrl+] ,取出当前光标下的word作为tag的名字并进行跳转。你需要查看main函数里的BOARD_InitPins()函数定义,直接在vim里,将光标移动到那里,然后ctrl+]就跳转过去了

实现的效果和Keil里点击Go to Definition 效果一致

看完了这个函数,想回到原来的地方怎么办呢,ctrl+t即可,对应Keil中下方红框向左的箭头功能。

但是只有ctags还不行,因为还有下面3个阅读代码过程中的问题没有解决

1)没有类似Keil中下方的工程文件列表,不方便随意选中某个文件浏览

2)没有类似Keil中下方的function功能,方便快速找到一个文件中的函数定义

3)如果浏览到下面这个文件的BOARD_InitPins函数,我想搜索谁调用它的,就没招了。

上述第一个问题,我们通过2.2节的Nerdtree工具实现,第二个问题通过2.3节的Taglist工具实现,第三个问题通过2.4节的cscope工具实现。

2.2安装配置Nerdtree

2.2.1Nerdtree安装

The NERD tree : A tree explorer plugin for navigating the filesystem

The NERD tree allows you to explore your filesystem and to open files and directories. It presents the filesystem to you in the form of a tree which you

manipulate with the keyboard and/or mouse. It also allows you to perform

simple filesystem operations.

安装方式是先在https://www.vim.org/scripts/script.php?script_id=1658网站下载压缩包,将解压缩的文件拷贝到~/.vim/中即可,下面是我~/.vim/中的文件:

如果没有.vim 目录的话,自己创建下即可。

2.2.2Nerdtree 使用

在使用前,现在~/.vimrc中添加以下两句话:

map <F2> :NERDTreeToggle<CR>

nnoremap <F3> :NERDTreeFind<CR>

具体什么用途,下文马上解释。

Nerdtree使用方式是在vim打开的文件中,切换到底线命令模式,输入NERDTree,回车就可以了。

显示效果如下,多出来左侧部分就是Nerdtree

如果需要关闭Nerdtree,需要切换到底线命令模式,输入NERDTreeClose, 这样操作显然很麻烦,这时上面map <F2> :NERDTreeToggle<CR> 这句话就起 作用了,我们只需要按F2键就可以来回切换打开与关闭该窗口了。

在Nerdtree打开的情况下,有两个窗口,默认打开后光标是在最左侧的窗口,怎么切换到右侧窗口浏览代码呢?通过Ctrl+w+w在两个窗口切换,我们先切换到右边的窗口,然后进入到CLOCK_EnableClock定义的文件里(还记得上一节的Ctrl-]快捷键吧),进入后显示如下

这时按一下F3快捷键,注意左侧窗口的变化,自动就定位到该函数所在的文件了,可以很清晰的看到所在文件的目录结构。这就是上面添加的第二句话的作用。

另外如果工程里文件很多,你想通过搜索快速找到某个文件,可以使用vim自带的find命令也能完成所需功能。find 会从 path 中搜索文件。所以在使用find之前一定要配置一下path变量(不是PATH环境变量)

具体方法是:在右侧窗口中,进入命令行窗口

:set path=./**

然后

find gpio_led_output.c或

find g[TAB]

即可搜索文件

2.3 安装配置Taglist

2.3.1 Taglist 安装

Taglist也是vim的一个插件,能将当前vim打开的文件中函数名、变量名等在一个窗口中列出来,并支持通过列出的函数名实现跳转。将Taglist下载下来的压缩包解压缩,将解压缩出来的doc里面的taglist.txt复制到~/.vim/doc/下面,plugin里面的taglist.vim文件拷贝到~/.vim/plugin目录下。这样Taglist这个插件安装完成了。

下载地址在:

https://www.vim.org/scripts/script.php?script_id=273

2.3.2 Taglist 使用

在使用前,现在~/.vimrc中添加以下两句话:

map <F4> :TlistToggle <CR>

let Tlist_Use_Right_Window = 1

其中第1句话是建了一个F4的快捷键,用来方便打开和和关闭该插件

第2句话是将该插件窗口放到最后侧。

另外根据需要你还可以选择是否添加以下特性:

let Tlist_Show_One_File = 1       “不同时显示多个文件的tag,只显示当前文件的
let Tlist_Exit_OnlyWindow = 1       “如果taglist窗口是最后一个窗口,则退出vim

按下F4打开Taglis后,整体窗口显示如下:

三部分窗口比例不是很和谐,中间代码窗口太窄,怎么调整窗口大小呢?

在~/.vimrc中添加以下两句话:

map <F6>  :vertical resize +1<CR>

map <F5>  :vertical resize -1<CR>

:vertical resize 是用来调整当前窗口宽度的。

我们在最左侧窗口中先按F5几次,减小当前窗口宽度,

然后到最右侧窗口中也按F5几次,减小当前窗口宽度,

就变成这样了:

另外你也可以在中间窗口通过Ctrl + – 减小字体来显示的更多内容

Ctrl + + 为增大字体

2.4 cscope

2.4.1 cscope安装配置

先看下ctags和cscope的区别:

ctags can be used to take you to the definition of a variable (e.g., a function, variable, or macro). cscope can be used to take you to the call site of a definition (e.g., all function calls, all variable uses, all macro uses).

简而言之,它是 ctags 的加强版,ctags 只能让我们跳转到某个 tag 的定义之处,但是无法让我们知道这个 tag 还在哪里出现过,或者被哪个函数调用过,这时候就需要 cscope 来完成该功能了。

安装方式如下:

sudo apt-get install cscope

验证是否安装成功的方式可以输入

cscope –version

表示安装成功

2.4.2 cscope 使用

第1步:使用 cscope 生成数据库文件

cscope -Rbkq

其中参数的含义:

-R 递归,对子目录也建立数据库

-b 只生成数据库,不进入 scope 界面

-k 生成数据库时,不搜索 /usr/include 目录

-q 生成 cscope.in.out 和 cscope.po.out 文件,加快查找速度

第2步:在vim命令行窗口输入:cs add ./cscope.out

第3步:

通用格式为 :cs find -option label

option 可以有很多种模式,在 Vim 中使用 :help cscope-find 来查看 option:

0 or s: Find this C symbol

1 or g: Find this definition

2 or d: Find functions called by this function

3 or c: Find functions calling this function

4 or t: Find this text string

6 or e: Find this egrep pattern

7 or f: Find this file

8 or i: Find files #including this file

比如:

cs find g BOARD_InitPins   会直接跳转到这个函数的定义处

cs find c BOARD_InitPins   会直接跳转到调用这个函数的地方

cs find t BOARD_InitPins   会列出以下5个出现该函数的地方

通过选择不同的数字,可以查看具体不同出现的位置。

这个搜索结果和Keil里搜索的结果一样:

但是这样使用有一个问题:就是我查看一个结果后,如果我还想继续查看其它的结果,还得重新搜索再选择一次。

该问题的解决方法是:

用cscopequickfix,在.vimrc中添加:

set cscopequickfix=c-,d-,e-,g-,i-,s-,t-

在命令行copen打开quickfix窗口,用cclose关闭,cprev、cnext移动

再次cs find t BOARD_InitPins 就会再右下角的窗口里显示,这样查看完一个文件后如果继续查看另外的文件,就可以通过最右下角的窗口切换选择。

这种方式打开的窗口是在右下角,看着不是很舒服,怎么弄成Keil那样放到最下方呢,在中间命令行窗口处输入以下内容,就可以在下方显示了

:botright copen

3.Uboot实战应用

先回到我们之前遇到的那个问题,我是如何在Uboot工程里找到需要修改的那个代码地方的,我们先把上一章改动后的0x0666666改回原来的0x66666666。

第1步:进入到Uboot所在目录

make cscope

第2步:

make ctags

注意上面两步骤没用第二章介绍的方法生成tags和cscope.out文件,原因是因为如果那样操作的话,就把uboot整个文件夹里的所有文件都加进去了,而使用make的方式只生成了实际用到的。

第3步:

vim

:cs add ./cscope.out

第4步:

F2 、F4 把Nerdtree和Taglist窗口打开,通过F5减小下两侧窗口宽度,Ctrl+-缩小字体

:botright copen  打开quickfix窗口

第5步:

:cs find t 0x66666666

我们找到16处,通过简单分析就可以定位到第一个结果就是我们需要的。

你如果是在整个目录去查询,就远远不止这16处了。

通过最右侧的Taglist窗口可以看到它是在board_mmc_init这个函数调用的。

紧接着我们看下是谁调用的这个函数board_mmc_init:

:cs find c board_mmc_init

我们把光标移动到board_mmc_init 处 Ctrl-t 一下,你会发现进入到不是刚才那个函数定义的地方了,变成了下面这里,这是咋回事??

我们再输入:ts board_mmc_init  查看下该函数的定义,发现竟然有三处

不过前两个是weak弱定义,所以直接Ctrl-t 跳转的就是上述的第一个结果

输入:tn 就会跳转到下一个定义,直到找到正确的定义

另外输入ts 就可以看到最后一次tag结果。

使用上面的方法就可以一步步的继续分析Uboot代码,这里不是本篇的重点,不详细介绍了。

4.结束语

本期相关的资料在https://github.com/TopSemic/NUC972_Linux  Lesson19中。

本篇为大家介绍了Linux下使用vim配合4个插件实现Linux代码的高效阅读,因为我也是刚学习,所以肯定有很多更好的使用方法还没有掌握,欢迎大家多交流,共同进步,可以在网页下方留言讨论,或者发邮件:Topsemic@sina.com ,微信公众号如下,欢迎关注:

5.参考内容

https://blog.csdn.net/Lius_1006/article/details/79524512

https://blog.csdn.net/cnh294141800/article/details/78208409

https://www.vim.org/scripts/script.php?script_id=1658

https://guqian110.github.io/pages/2015/01/25/learning_vim_ctags_cscope_taglist.html

https://blog.csdn.net/dengxiayehu/article/details/6330200

https://yang3wei.github.io/blog/2013/01/29/nerdtree-kuai-jie-jian-ji-lu/

https://zhuanlan.zhihu.com/p/85040099

https://blog.csdn.net/jiayu/article/details/2418147

https://blog.csdn.net/menggucaoyuan/article/details/12950711

https://www.cnblogs.com/luosongchao/p/3254451.html

https://blog.csdn.net/dengxiayehu/article/details/6330200

https://courses.cs.washington.edu/courses/cse451/10au/tutorials/tutorial_ctags.html

https://www.thinbug.com/q/6726783

https://ricostacruz.com/til/navigate-code-with-ctags

https://andrew.stwrt.ca/posts/vim-ctags/

https://courses.cs.washington.edu/courses/cse451/10au/tutorials/tutorial_ctags.html

https://ricostacruz.com/til/navigate-code-with-ctags

https://medium.com/usevim/nerd-tree-guide-bb22c803dcd2

https://catonmat.net/vim-plugins-nerdtree-vim

https://vim.fandom.com/wiki/Resize_splits_more_quickly

https://vi.stackexchange.com/questions/773/how-do-i-change-the-default-size-of-plugin-window-nerdtree-taglist-etc

https://blog.csdn.net/qq_16777851/article/details/81782669

0

发表评论