嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼


1.引言


如何閱讀代碼還要單獨寫一篇文章?難道不是隨便用一個IDE就可以了嗎?回到上一篇文章裡介紹的那個問題,需要修改uboot裡board_mmc_init函數里的writel(0x66666666,REG_MFP_GPD_L) ,對於初學者如何在uboot代碼裡找到這句話呢?當時問我這個問題的網友就有這個困惑。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

因為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

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

使用ctags --list-languages可以查看ctags支持的編程語言

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

使用ctags --list-maps可以查看ctags支持的編程語言對應的文件擴展名

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

使用ctags --list-kinds可以查看ctags識別的語法元素,使用ctags --list-kinds=c可單查看C語言識別的語法元素

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

安裝完成之後,想要使用ctags,必須在你想要查看的代碼目錄中有tags文件。2.1.2 ctags使用在執行下述操作前,已經在Ubuntu裡~/mcuproject目錄下放了一個MCU的工程。第1步: ctags -R *生成tags文件。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

第2步:需要找到main函數定義在哪裡,先輸入vim打開vim窗口,然後在vim命令行窗口輸入ts main它的作用是:Search for a particular tag

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

再根據提示輸入1 回車就跳轉到main函數所在的位置了,

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

有一點需要大家注意的是:一定要在tags所在的目錄打開vim,輸入ts才能搜到你要找到的tag,在其它目錄是不行的,比如我進入到上一級目錄,就會提示如下信息了。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

對比下Windows裡Keil環境下,我通常用如下方式去查找:

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

搜索結果如下:

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

看到這裡你是不是有個疑問,為什麼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+]就跳轉過去了

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

實現的效果和Keil裡點擊Go to Definition 效果一致

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

看完了這個函數,想回到原來的地方怎麼辦呢,ctrl+t即可,對應Keil中下方紅框向左的箭頭功能。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

但是隻有ctags還不行,因為還有下面3個閱讀代碼過程中的問題沒有解決1)沒有類似Keil中下方的工程文件列表,不方便隨意選中某個文件瀏覽

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

2)沒有類似Keil中下方的function功能,方便快速找到一個文件中的函數定義

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

3)如果瀏覽到下面這個文件的BOARD_InitPins函數,我想搜索誰調用它的,就沒招了。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

上述第一個問題,我們通過2.2節的Nerdtree工具實現,第二個問題通過2.3節的Taglist工具實現,第三個問題通過2.4節的cscope工具實現。

2.2安裝配置Nerdtree

2.2.1 Nerdtree安裝

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/中的文件:

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

如果沒有.vim 目錄的話,自己創建下即可。

2.2.2 Nerdtree使用

在使用前,現在~/.vimrc中添加以下兩句話:

map :NERDTreeToggle

nnoremap :NERDTreeFind

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

具體什麼用途,下文馬上解釋。Nerdtree使用方式是在vim打開的文件中,切換到底線命令模式,輸入NERDTree,回車就可以了。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

顯示效果如下,多出來左側部分就是Nerdtree

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

如果需要關閉Nerdtree,需要切換到底線命令模式,輸入NERDTreeClose,這樣操作顯然很麻煩,這時上面map :NERDTreeToggle 這句話就起作用了,我們只需要按F2鍵就可以來回切換打開與關閉該窗口了。在Nerdtree打開的情況下,有兩個窗口,默認打開後光標是在最左側的窗口,怎麼切換到右側窗口瀏覽代碼呢?通過Ctrl+w+w在兩個窗口切換,我們先切換到右邊的窗口,然後進入到CLOCK_EnableClock定義的文件裡(還記得上一節的Ctrl-]快捷鍵吧),進入後顯示如下

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

這時按一下F3快捷鍵,注意左側窗口的變化,自動就定位到該函數所在的文件了,可以很清晰的看到所在文件的目錄結構。這就是上面添加的第二句話的作用。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

另外如果工程裡文件很多,你想通過搜索快速找到某個文件,可以使用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 :TlistToggle

let Tlist_Use_Right_Window = 1

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

其中第1句話是建了一個F4的快捷鍵,用來方便打開和和關閉該插件第2句話是將該插件窗口放到最後側。另外根據需要你還可以選擇是否添加以下特性:

let Tlist_Show_One_File = 1 "不同時顯示多個文件的tag,只顯示當前文件的
let Tlist_Exit_OnlyWindow = 1 "如果taglist窗口是最後一個窗口,則退出vim

按下F4打開Taglis後,整體窗口顯示如下:

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

三部分窗口比例不是很和諧,中間代碼窗口太窄,怎麼調整窗口大小呢?在~/.vimrc中添加以下兩句話:

map :vertical resize +1

map :vertical resize -1

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

:vertical resize 是用來調整當前窗口寬度的。

我們在最左側窗口中先按F5幾次,減小當前窗口寬度,

然後到最右側窗口中也按F5幾次,減小當前窗口寬度,

就變成這樣了:

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

另外你也可以在中間窗口通過Ctrl + - 減小字體來顯示的更多內容

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

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

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

表示安裝成功

2.4.2 cscope使用

第1步:使用 cscope 生成數據庫文件

cscope -Rbkq

其中參數的含義:

-R 遞歸,對子目錄也建立數據庫

-b 只生成數據庫,不進入 scope 界面

-k 生成數據庫時,不搜索 /usr/include 目錄

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

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

第2步:在vim命令行窗口輸入:cs add ./cscope.out第3步:通用格式為 :cs find -option labeloption 可以有很多種模式,在 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個出現該函數的地方

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

通過選擇不同的數字,可以查看具體不同出現的位置。這個搜索結果和Keil裡搜索的結果一樣:

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

但是這樣使用有一個問題:就是我查看一個結果後,如果我還想繼續查看其它的結果,還得重新搜索再選擇一次。該問題的解決方法是:用cscopequickfix,在.vimrc中添加:set cscopequickfix=c-,d-,e-,g-,i-,s-,t-

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

在命令行copen打開quickfix窗口,用cclose關閉,cprev、cnext移動再次cs find t BOARD_InitPins 就會再右下角的窗口裡顯示,這樣查看完一個文件後如果繼續查看另外的文件,就可以通過最右下角的窗口切換選擇。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

這種方式打開的窗口是在右下角,看著不是很舒服,怎麼弄成Keil那樣放到最下方呢,在中間命令行窗口處輸入以下內容,就可以在下方顯示了:botright copen

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

3.Uboot實戰應用

先回到我們之前遇到的那個問題,我是如何在Uboot工程裡找到需要修改的那個代碼地方的,我們先把上一章改動後的0x0666666改回原來的0x66666666。第1步:進入到Uboot所在目錄make cscope

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

第2步:make ctags

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

注意上面兩步驟沒用第二章介紹的方法生成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處,通過簡單分析就可以定位到第一個結果就是我們需要的。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

你如果是在整個目錄去查詢,就遠遠不止這16處了。通過最右側的Taglist窗口可以看到它是在board_mmc_init這個函數調用的。緊接著我們看下是誰調用的這個函數board_mmc_init::cs find c board_mmc_init

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

我們把光標移動到board_mmc_init 處 Ctrl-t 一下,你會發現進入到不是剛才那個函數定義的地方了,變成了下面這裡,這是咋回事??

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

我們再輸入:ts board_mmc_init 查看下該函數的定義,發現竟然有三處

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

不過前兩個是weak弱定義,所以直接Ctrl-t 跳轉的就是上述的第一個結果輸入:tn 就會跳轉到下一個定義,直到找到正確的定義

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

另外輸入ts 就可以看到最後一次tag結果。

嵌入式Linux系列第19篇:如何高效的閱讀Linux源碼

使用上面的方法就可以一步步的繼續分析Uboot代碼,這裡不是本篇的重點,不詳細介紹了。

4.結束語

本篇為大家介紹了Linux下使用vim配合4個插件實現Linux代碼的高效閱讀,因為我也是剛學習,所以肯定有很多更好的使用方法還沒有掌握,歡迎大家多交流,共同進步,可以在網頁下方留言討論

想要獲取本文pdf版本的,請私信留言。


分享到:


相關文章: