Linux内核开发的Vim配置

35

内核开发实际上与传统的C项目开发有所不同(从我这个新手的角度来看)。因此,我一直想知道内核黑客的vim配置是什么。

最重要的是如何在vim中导航内核源代码树。我尝试过ctags,但是效果非常糟糕。

是否有人可以给我一些提示?

1个回答

104

从开发者的角度来看,Linux内核和常规C项目之间的主要区别如下:

  • 内核是一个非常庞大的项目(因此您应该选择要索引的代码)
  • 它具有架构相关的代码(您每次只对一个特定的架构感兴趣;其他架构不应被索引)
  • 它有非常具体的编码风格,您应该遵循(vim应该配置为相应地显示代码)
  • 它不使用C标准库,而是有自己类似的例程(因此您的索引工具不应该索引libc头文件)

安装索引工具

为了浏览内核代码,我建议使用cscopectags工具。要安装它们,请运行以下命令:

$ sudo aptitude install cscope exuberant-ctags

简单解释一下:

  • cscope:用于浏览代码(切换函数等)。它能够跳转到符号定义,查找所有符号用法等。
  • ctags:需要用于Tagbar插件(稍后将进行讨论)和vim中的自动完成机制Omni completion;也可以用于导航。对于C代码导航,ctags并不像cscope那样好,因为ctags只能跳转到符号定义而不能跳转到其调用者。

创建索引数据库

现在,您应该对内核源文件进行索引。这里有两种方法:手动创建索引或使用内核中可用的脚本。如果您不确定哪种方法最适合您,建议使用内核脚本,因为它在幕后执行了许多巧妙的技巧(例如忽略未构建的源文件并将标头文件移动到结果列表的顶部)。

但首先,为您的体系结构/板配置和构建内核,因为构建文件可以稍后用于改善索引过程。

使用 scripts/tags.sh 进行索引

内核有一个相当好的脚本(scripts/tags.sh)用于创建内核索引数据库。应该使用 make cscopemake tags 规则来创建索引,而不是直接运行该脚本。

示例:

$ make O=. ARCH=arm SUBARCH=omap2 COMPILED_SOURCE=1 cscope tags

这里是关于 where 的一些参数解释:

  • O=. - 使用绝对路径(如果你希望在内核目录之外加载已创建的 cscope/ctags 索引文件,比如用于开发目录中的 out-of-tree 内核模块),如果你只想在内核目录中进行开发,则可以省略该参数。
  • ARCH=... - 选择要索引的 CPU 架构。参见 arch/ 目录下的子目录。例如,如果 ARCH=arm,则会索引 arch/arm/ 目录,其余的 arch/* 目录将被忽略。
  • SUBARCH=... - 选择要索引的子架构(即与板相关的文件)。例如,如果 SUBARCH=omap2,则只会索引 arch/arm/mach-omap2/arch/arm/plat-omap/ 目录,其余的机器和平台将被忽略。
  • COMPILED_SOURCE=1 - 只索引编译过的文件。通常你只对你构建使用的源文件感兴趣(因此是编译的)。如果你还想索引未构建的文件,只需省略此选项即可。
  • cscope - 用于创建 cscope 索引的规则
  • tags - 用于创建 ctags 索引的规则

手动索引

内核脚本 (tags.sh) 可能无法正确工作,或者你可能想更好地控制索引过程。在这些情况下,你应该手动索引内核源代码。

手动索引的见解可从这里获得。

首先需要创建 cscope.files 文件,其中列出了你要索引的所有文件。例如,我使用以下命令列出 ARM 架构的文件 (arch/arm),特别是针对 OMAP 平台的 (排除其他平台以保持导航易用性):

find    $dir                                          \
        -path "$dir/arch*"               -prune -o    \
        -path "$dir/tmp*"                -prune -o    \
        -path "$dir/Documentation*"      -prune -o    \
        -path "$dir/scripts*"            -prune -o    \
        -path "$dir/tools*"              -prune -o    \
        -path "$dir/include/config*"     -prune -o    \
        -path "$dir/usr/include*"        -prune -o    \
        -type f                                       \
        -not -name '*.mod.c'                          \
        -name "*.[chsS]" -print > cscope.files
find    $dir/arch/arm                                 \
        -path "$dir/arch/arm/mach-*"     -prune -o    \
        -path "$dir/arch/arm/plat-*"     -prune -o    \
        -path "$dir/arch/arm/configs"    -prune -o    \
        -path "$dir/arch/arm/kvm"        -prune -o    \
        -path "$dir/arch/arm/xen"        -prune -o    \
        -type f                                       \
        -not -name '*.mod.c'                          \
        -name "*.[chsS]" -print >> cscope.files
find    $dir/arch/arm/mach-omap2/                     \
        $dir/arch/arm/plat-omap/                      \
        -type f                                       \
        -not -name '*.mod.c'                          \
        -name "*.[chsS]" -print >> cscope.files

对于x86架构(arch/x86),你可以使用类似下面的代码:

find    $dir                                          \
        -path "$dir/arch*"               -prune -o    \
        -path "$dir/tmp*"                -prune -o    \
        -path "$dir/Documentation*"      -prune -o    \
        -path "$dir/scripts*"            -prune -o    \
        -path "$dir/tools*"              -prune -o    \
        -path "$dir/include/config*"     -prune -o    \
        -path "$dir/usr/include*"        -prune -o    \
        -type f                                       \
        -not -name '*.mod.c'                          \
        -name "*.[chsS]" -print > cscope.files
find    $dir/arch/x86                                 \
        -path "$dir/arch/x86/configs"    -prune -o    \
        -path "$dir/arch/x86/kvm"        -prune -o    \
        -path "$dir/arch/x86/lguest"     -prune -o    \
        -path "$dir/arch/x86/xen"        -prune -o    \
        -type f                                       \
        -not -name '*.mod.c'                          \
        -name "*.[chsS]" -print >> cscope.files

dir变量可能有以下几个值:

  • .:如果你只在内核源代码目录中工作,那么这些命令应该从内核源代码的根目录运行。
  • 内核源代码目录的绝对路径:如果你要开发一些 out-of-tree 内核模块,那么脚本可以从任何地方运行。

我使用第一个选项 (dir=.),因为我没有开发任何 out-of-tree 模块。

现在当 cscope.files 文件准备好后,我们需要运行实际的索引:

$ cscope -b -q -k

-k参数告诉cscope不要索引C标准库(因为内核并不使用它)。

现在是创建ctags索引数据库的时候了。为了加速此过程,我们将重用已经创建的cscope.files

$ ctags -L cscope.files

好的,cscopectags索引数据库已经建立完成,你可以删除cscope.files文件,因为我们不再需要它了:

$ rm -f cscope.files

下一个文件包含索引数据库(用于 cscope 和 ctags ):
- cscope.in.out
- cscope.out
- cscope.po.out
- tags

将它们放在内核源代码目录的根目录中。

Vim插件

注意:接下来我会展示如何使用pathogen处理Vim插件。但是现在Vim 8已经发布,可以使用本地包加载来达到相同的目的。

接下来我们将安装一些Vim插件。为了更好地掌握它,我鼓励您使用pathogen插件。它允许您只需将vim插件git clone到您的~/.vim/bundle/并使其保持隔离,而不是将来自不同插件的文件混合在~/.vim目录中。

这里描述的那样安装pathogen

不要忘记执行下面的操作(就像在同一个链接中描述的那样):

Add this to your vimrc:

execute pathogen#infect()

If you're brand new to Vim and lacking a vimrc, vim ~/.vimrc and paste in the following super-minimal example:

execute pathogen#infect()
syntax on
filetype plugin indent on

安装Vim的cscope地图

Vim已经支持cscope(请参阅:help cscope)。您可以使用命令,如:cs f g kfree跳转到符号或文件。但这样不太方便。为了加速操作,您可以使用快捷方式(将光标放在某个函数上,按下某个键组合并跳转到该函数)。要添加cscope的快捷方式,您需要获取cscope_maps.vim文件。

要使用pathogen安装它,您只需将存储库克隆到您的~/.vim/bundle中即可:

$ git clone https://github.com/joe-skb7/cscope-maps.git ~/.vim/bundle/cscope-maps

现在你应该能够使用快捷键在vim中浏览函数和文件。打开一些内核源文件,在某个函数调用上放置键盘光标,然后按下Ctrl+\,再按g键。它应该会带您到函数实现处。或者它可以显示所有可用的函数实现,然后您可以选择要使用的函数:cscope-struct
有关其余密钥映射,请参见cscope_maps.vim文件。
您还可以在vim中使用命令,例如:
:cs f g kmalloc

请查看:help cscope以获取详细信息。

ctags注意事项

即使在查找某些#define声明时,ctags仍可以用于导航。您可以将光标放在此定义的使用上,然后按g,接着按Ctrl+]。详情请参见此答案

cscope注意事项

下一个技巧可用于在内核中查找结构体声明:

:cs f t struct device {

请注意,上述命令依赖于特定的结构声明样式(用于内核),因此我们知道结构声明始终具有此形式:struct some_stuct {。这个技巧可能在其他编码风格的项目中无法使用。

开发外部模块注意事项

如果您正在开发外部模块,则可能需要从内核目录加载cscopectags数据库。可以通过以下命令在vim中(命令模式下)完成:

加载外部cscope数据库:

:cs add /path/to/your/kernel/cscope.out

加载外部ctags数据库:

:set tags=/path/to/your/kernel/tags

vimrc

为了更好地支持内核开发,您需要对~/.vimrc进行一些修改。

首先,让我们用垂直线突出显示第81列(因为内核编码要求您将行长度保持在80个字符以内):

" 80 characters line
set colorcolumn=81
"execute "set colorcolumn=" . join(range(81,335), ',')
highlight ColorColumn ctermbg=Black ctermfg=DarkRed

如果您想将80+列也标记出来,请取消第二行的注释。

末尾空格在内核编码风格中是被禁止的,因此您可能希望将它们标记出来:

" Highlight trailing spaces
" http://vim.wikia.com/wiki/Highlight_unwanted_spaces
highlight ExtraWhitespace ctermbg=red guibg=red
match ExtraWhitespace /\s\+$/
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
autocmd BufWinLeave * call clearmatches()

内核编码风格

为了使vim遵循内核编码风格,你可以使用现成的插件:vim-linux-coding-style

有用的插件

下面的插件通常被广泛使用,所以您可能也会觉得它们很有用:

以下是一些有趣的插件,但您可能需要根据内核进行配置:

Omni自动补全

Vim 7(及以上版本)已经内置了自动完成支持,它称之为Omni自动补全。详见:help new-omni-completion

在内核这样的大型项目上,Omni自动补全的速度相对较慢。如果您仍然想使用它,可以将以下行添加到您的~/.vimrc文件中以启用它:

" Enable OmniCompletion
" http://vim.wikia.com/wiki/Omni_completion
filetype plugin on
set omnifunc=syntaxcomplete#Complete

" Configure menu behavior
" http://vim.wikia.com/wiki/VimTip1386
set completeopt=longest,menuone
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
inoremap <expr> <C-n> pumvisible() ? '<C-n>' :
  \ '<C-n><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>'
inoremap <expr> <M-,> pumvisible() ? '<C-n>' :
  \ '<C-x><C-o><C-n><C-p><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>'

" Use Ctrl+Space for omni-completion
" https://dev59.com/ZnRB5IYBdhLWcg3wyqKo
inoremap <expr> <C-Space> pumvisible() \|\| &omnifunc == '' ?
  \ "\<lt>C-n>" :
  \ "\<lt>C-x>\<lt>C-o><c-r>=pumvisible() ?" .
  \ "\"\\<lt>c-n>\\<lt>c-p>\\<lt>c-n>\" :" .
  \ "\" \\<lt>bs>\\<lt>C-n>\"\<CR>"
imap <C-@> <C-Space>

" Popup menu hightLight Group
highlight Pmenu ctermbg=13 guibg=LightGray
highlight PmenuSel ctermbg=7 guibg=DarkBlue guifg=White
highlight PmenuSbar ctermbg=7 guibg=DarkGray
highlight PmenuThumb guibg=Black

" Enable global scope search
let OmniCpp_GlobalScopeSearch = 1
" Show function parameters
let OmniCpp_ShowPrototypeInAbbr = 1
" Show access information in pop-up menu
let OmniCpp_ShowAccess = 1
" Auto complete after '.'
let OmniCpp_MayCompleteDot = 1
" Auto complete after '->'
let OmniCpp_MayCompleteArrow = 1
" Auto complete after '::'
let OmniCpp_MayCompleteScope = 0
" Don't select first item in pop-up menu
let OmniCpp_SelectFirstItem = 0

使用 Ctrl+Space 进行自动完成。

外观效果

256 色

首先,您要确保您的终端支持 256 种颜色。例如,可以使用 urxvt-256 终端来实现此目的。对于 gnome-terminal,您只需将下一行添加到您的 ~/.bashrc 中即可:

export TERM="xterm-256color"

完成后,将下面这行代码加入到你的~/.vimrc文件中:

set t_Co=256

配色方案

现在下载您喜欢的配色方案到~/.vim/colors文件夹,并在~/.vimrc中选择它们:

set background=dark
colorscheme hybrid

使用哪种颜色方案是一个非常主观的问题。我建议初学者可以尝试使用mrkn256hybridsolarized这几个选项。

字体

有很多适合编程的好字体。许多Linux程序员使用Terminus字体,您可以尝试使用它来起步。

已知缺陷

在vim中仍然存在一些缺陷。
  1. cscope/ctags不能使用include/generated/autoconf.h中的定义,并忽略未构建的代码。将所有代码索引以将其用作编码参考仍然可能是有用的。
  2. 没有宏扩展(好吧,有一些函数(基于gcc -E),但我不确定它是否适用于内核)。
我所知道的唯一能够解决这些问题的IDE是带有CDT的Eclipse

2
那是一个糟糕的问题(太泛泛,容易出现不同意见),但你的回答是 Vim 配置的一个良好概述和介绍! - Xavier T.
内核的Makefile中已经有一个目标cscope。使用make cscope即可完成任务。 - 0andriy
我不确定这个规则究竟索引了哪些文件。它怎么知道我使用的是哪种架构,哪个平台...如果您了解一些信息,请提供详细说明。也许这个规则对于x86架构已经足够了。但例如对于ARM来说,你应该真正选择要索引什么,因为大多数文件与你特定的平台无关。在理想情况下,我更喜欢只索引与我的特定构建相关的文件(甚至包括驱动程序)。其余的代码可以提供良好的参考,但只有在索引下关键文件时,开发才会更容易。 - Sam Protsenko
1
@AndyShevchenko 感谢您指出 make cscope,我看了一下。决定继续使用手动命令列出文件,但是真的改进了它们(也在我的答案中进行了更改)。我的命令(现在)似乎会产生更少的索引文件(特别是针对ARM架构),而且它们也更加灵活(例如,我删除了kvmxen子系统的索引,因为我不使用它们)。 - Sam Protsenko
1
@AndyShevchenko在回答中添加了使用make cscope的备注。谢谢! - Sam Protsenko
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接