使用vim打造内核模块开发环境

使用vim打造内核模块开发环境

写在前面

我一直犹豫要不要写本文,因为本人水平有限,无法把内核模块(下面简称ko)的开发环境搞得像C++开发环境那么炫酷,(C++开发环境的搭建可以见我这个文章:https://zp001.blog.csdn.net/article/details/138594804),也没有自信我这个方案就是最好的方案,只是自己用得比较顺手而已。
这里面有客观原因,也有主观原因:

  1. 客观原因
    1. KO开发不是你完整开发一个程序,而是要在别人的项目(Linux)中使用别人的Makefile和源码开发一个小模块,所以编译你开发的这个小模块依赖于整个Linux源码的Makefile,而非单单那个KO的Makefile;再者,编译KO是有严格的编译器要求的,即是说,要使用编译Linux内核的那个编译器(gcc),并且要使用那个gcc版本。另一方面,我们知道,在vim中使用lsp,其实是依靠clangd这个东西时时刻刻在编译你的源码,这意味着你要配置clangd使用特定版本的gcc在特定的Linux内核环境中进行编译,这个折腾的难度是挺高的。
    2. 一般情况下,KO开发都要适配好几个版本的Linux内核,这意味着你得依赖于好几个版本的Linux源码和GCC。这意味着,就算你折腾clangd,使得它能实时编译某个版本的源码,也难以适配其他版本的源码。
  2. 主观原因:
    1. 主观的原因在于,迎着上述障碍去折腾,必要性也不是太大。KO开发是一门老手艺,存在于行业几十年了,这说明老一辈程序员是能获得足够的工具支撑的,这便是vim的tagging技术。tag技术就是把C代码的元素打上各种标签,分辨出哪些字符是函数名、哪些字符是结构体名字等等,说白了,就是关键字符提取。可想而知,它其实是不懂C语言的,效果自然比不上LSP,但是由于C语言是一门简单的语言,不像C++那么复杂,所以KO开发中,仍然是足够好的工具。
    2. 使用基于tag技术的vim,搭建开发环境比较简单,就算我们在多个Linux搭建多套环境也不会太麻烦。这在适配多个Linux内核的工作中,是比较方便的。
    3. 最后一个主观原因是,开发KO和开发web app不太一样,开发者需要非常严谨地对待自己的代码,使得自己的代码水平接近于Linux内核源码的水平,这要求是非常高的。这个要求会使得你会非常熟悉自己的代码,哪个struct里面有哪些成员是烂熟于胸的,这会减少对“自动补全”这类功能的需求。
      总之,我对内核模块的开发环境,原则是“够用”,而不是“完美”。

内核模块开发环境搭建思路

搭建思路是:

  1. 熟练掌握VIM的内置功能+使用tag技术+熟记自己的代码=良好的KO开发环境
  2. 使用一个桌面linux,我选fedora,通过podman打造多个Linux内核的初步开发和编译环境;等写完代码、能通过多个容器的编译后,再分发到各个具体的要发布的Linux版本上去调试和发布。所以vim的环境是需要多套的。

下面具体讲在fedora上面如何搭建开发环境。

内核模块开发环境的方案概要

如上所述,我的方案是:

  • vim选择:原生vim(不是neovim)+插件管理器+tag插件+tag工具
  • 开发linux选择:fedora39
  • 多版本内核编译:fedora39自带的podman+真正的目的linux版本虚拟机编译和调试

其中vim方案中,用到的软件如下:

工具/插件说明备注
vim本文中,vim指vimx,通过fedora自带的repo安装注1
gtagsGNU Global source code tagging system,就靠它识别代码了,通过fedora自带的repo安装注2
python3通过fedora自带的repo安装,版本12注3
vim-plugvim的插件管理器,下文讲安装办法
gutentags自动管理gtags,通过插件管理器安装
leaderf模糊查找神器,跳转神器,通过插件管理器安装注4
kana-textobject系列增强vim的textobject功能,通过插件管理器安装
sgur/vim-textobj-parameter增强vim的textobject功能,通过插件管理器安装
vim-easymotion在vim中快速跳转,通过插件管理器安装
nerdtree文件夹浏览,通过插件管理器安装
taglist函数列表插件,可选,下文讲安装办法
gtags.vimgtags的vim插件,可选,下文讲安装办法

注1:vimx比vim多了系统粘贴板等图形化界面的功能,通过yum -y install vim-X11安装
注2:安装办法yum -y install global-6.6.5-11.fc39.x86_64
注3:Leaderf插件需要用到这个软件包sudo yum -y install python-devel
注4:Leaderf依赖于ripgrep,通过yum -y install ripgrep安装
有这些,在功能上就足够了,在这里就不多说其他美化的插件了。

vim开发环境的搭建

vim有插件管理器可以自动安装和管理vim插件,但是有三个东西不能通过插件管理器安装,分别是:

  1. 插件管理器本身
  2. gtags的vim插件
  3. taglist插件

插件管理器vim-plug

这里选择vim-plug插件管理器,算主流选择吧。
项目地址是:https://github.com/junegunn/vim-plug
安装办法很简单,一句命令即可:

curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

gtags的vim插件

当安装好global之后,其实就自带这个插件了,只需复制到vim相关目录即可:

cp /usr/share/gtags/gtags.vim $HOME/.vim/plugin

taglist插件

这是个老插件了。
在有Leaderf之后,使用Leaderf function命令比它好用多了,这个插件就用得很少了。
安装办法也很简单,在下面网址中下载,然后解压到~/.vim,确保~/.vim/plugin/taglist.vim文件存在即可。
https://www.vim.org/scripts/script.php?script_id=273

安装过程

❯ pwd
/home/zp001/.vim
❯ unzip taglist_46.zip
Archive:  taglist_46.zip
  inflating: plugin/taglist.vim
  inflating: doc/taglist.txt

leaderf function效果

leaderf

taglist效果

taglis

Leaderf function方便的地方

leaderf可以模糊搜索,定位函数更方便

使用插件管理器安装其他插件

编辑.vimrc加入以下内容

call plug#begin()
" 必装插件
Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
Plug 'Yggdroot/LeaderF', { 'do': ':LeaderfInstallCExtension' }
Plug 'ludovicchabant/vim-gutentags'
Plug 'kana/vim-textobj-user'
Plug 'kana/vim-textobj-indent'
Plug 'kana/vim-textobj-syntax' " key: y
Plug 'kana/vim-textobj-function', { 'for':['c', 'cpp', 'vim', 'java'] } " key: f/F, dont work with cpp
Plug 'sgur/vim-textobj-parameter' " key: ,
Plug 'easymotion/vim-easymotion'
" 其他建议安装的插件
Plug 'ghifarit53/tokyonight-vim' " vim主题
Plug 'terryma/vim-multiple-cursors' " 多光标操作,在代码中重命名变量什么的场景很有用
Plug 'tpope/vim-commentary' " 快速注释代码
Plug 'vim-airline/vim-airline' " 更好看的状态栏
Plug 'vim-airline/vim-airline-themes' " 更好看的状态栏
call plug#end()

重新打开vim,输入命令:PlugInstall,即可看到插件管理器在下载安装所有插件

插件的配置和使用

vim-gutentags、Leaderf、gtags.vim的配置

刚接触这些插件的时候,vim-gutentags、Leaderf、gtags.vim是很容易让人混淆的,我当初也概莫能外,这里澄清一下。

  1. vim-gutentags是自动生成gtags索引文件的插件
  2. Leaderf是模糊搜索的插件,它可以使用gtags的索引文件
  3. gtags.vim是正宗的gtags索引文件的使用者。
    上文已经提到,gtags.vim是可以不装的。
解决vim-gutentags和Leaderf冲突

关于这3个东西,第一件事要提的是:vim-gutentags和Leaderf这两个插件的功能有冲突。他们俩都会自动生成gtags索引文件,幸好Leaderf预料到了此种情况并给出了办法。

文档地址:https://github.com/Yggdroot/LeaderF/blob/master/doc/leaderf.txt

g:Lf_GtagsGutentags                             *g:Lf_GtagsGutentags*
    if you use https://github.com/ludovicchabant/vim-gutentags to generate
    gtags; Firstly, you should let g:Lf_GtagsAutoGenerate = 0 and let g:Lf_GtagsGutentags = 1.
    Then, you should config gutentags like this: g:Lf_CacheDirectory = expand('~')
    and g:gutentags_cache_dir = expand(g:Lf_CacheDirectory.'/LeaderF/gtags')
    Default value is 0

我的配置如下:

" for Leaderf
let g:Lf_GtagsAutoGenerate = 0
let g:Lf_GtagsGutentags = 1
let g:Lf_CacheDirectory = expand('~')


" for gutentags
let g:gutentags_modules = ['ctags', 'gtags_cscope'] " 让gutentags自动产生ctags和gtags的索引文件
let g:gutentags_project_root = ['.root'] " gutentags把有.root文件的目录当成是项目的根目录,来自动帮你管理索引文件
let g:gutentags_cache_dir = expand(g:Lf_CacheDirectory.'/LeaderF/gtags')

" for gtags.vim
set cscopeprg='gtags-cscope' " 使用 gtags-cscope 代替 cscope,这会enable老的cscope命令
let Gtags_Close_When_Single = 1 " 如果搜索只有一个,就不打开搜索结果窗口啦,直接跳转

澄清gtags.vim和Leaderf的冲突

gtags.vim和Leaderf都是gtags索引文件的使用者,功能上有重叠的地方。但是Leaderf除了可以使用gtags索引,还可以使用ripgrep,功能比gtags.vim更多,所以相较之下,要二选一的话就选Leaderf了。
我使用gtags已经很长时间了,所以舍不得丢弃,其实两者是可以共存的,并无冲突。

我的配置如下:

" for gtags.vim
map <C-\><C-]> :GtagsCursor<CR>
nmap <C-\>d :Gtags -d <C-R>=expand("<cword>")<CR>
nmap <C-\>r :Gtags -r <C-R>=expand("<cword>")<CR>
nmap <C-\>g :Gtags -g <C-R>=expand("<cword>")<CR>
nmap <C-\>f :Gtags -f <C-R>=expand("<cword>")<CR>

" for Leaderf
nmap <F7> :Leaderf file --popup
nmap <F8> :Leaderf rg --popup
nmap <leader>fn :Leaderf function<CR>
nmap <Leader>fg :Leaderf rg <C-R>=expand("<cword>")<CR>
nmap <Leader>fe :Leaderf rg -F <C-R>=expand("<cword>")<CR>
noremap <leader>gr :<C-U><C-R>=printf("Leaderf gtags -r %s --auto-jump", expand("<cword>"))<CR>
noremap <leader>gd :<C-U><C-R>=printf("Leaderf gtags -d %s --auto-jump", expand("<cword>"))<CR>
noremap <leader>go :<C-U><C-R>=printf("Leaderf gtags --recall %s", "")<CR>
noremap <leader>gn :<C-U><C-R>=printf("Leaderf gtags --next %s", "")<CR>
noremap <leader>gp :<C-U><C-R>=printf("Leaderf gtags --previous %s", "")<CR>

好了,现在gtags和Leaderf都配置好了,那日常中想用哪个都可以了。

gutentags的使用

这是个装完就不用管的插件,真爽。
它负责自动更新gtags索引,它做到了,我们负责写代码,它在后台默默更新,不打扰我们。

跳转到函数定义

跳转到函数定义是最常用的操作需求了吧。现在有4种办法可以做到了。

  1. 使用ctags快捷键,转到当前光标所在的词的定义。C+]

  2. 使用gtags快捷键,转到当前光标所在的词的定义。<C-\><C-]>

  3. 使用gtags快捷键,可以输入一个词,转到它的定义,默认是当前光标所在词。<C-\>d

  4. 使用Leaderf快捷键,可以输入一个词,转到它的定义,默认是当前光标所在词。<leader>gd
    好了,跳过去学会了,那怎么跳回来呢?C-o
    还有一个常见的场景,我跳过去函数定义,只是为了查看函数的参数,我还是要编辑当前行的代码来调用这个函数的,怎么操作才方便呢?
    我的办法是,先:vs命令左右分屏,然后在右侧屏幕C+]调到函数定义,然后回到左侧屏幕编写代码。
    jumptofunction

    虽然有点土,但是用惯了也还好。

注:C是ctrl键

搜索调用函数的位置

现在有两个办法来搜索了。

  1. 使用gtags。<C-\>
  2. 使用Leaderf。<leader>gr
    两个办法的区别在于。gtags会吧搜索结果放在quickfix窗口里,Leaderf则会上下分屏显示。

使用Leaderf随意搜索

如果没有Leaderf,那么在项目中随意搜索某个字符,就要使用grep或者vimgrep命令,有了Leaderf之后,功能没变化,但界面就变炫酷很多了,性能也提升了,因为它调用了rg命令来加速。
比如现在可以按<F8>弹出浮窗来搜索,还可以实时预览。
livegrep

如果不喜欢浮窗这种浮夸的展现方式(比如我),就使用<Leader>fg,它会低调地上下分屏展示,老实说,这种方式实用很多。
livegrep2

这功能或许是使用Leaderf的最大原因吧。

使用vim-easymotion来快速跳转

vim是全键盘使用的,但不用鼠标的话,遇到要大幅度移动光标就比较麻烦。传统vim的移动办法有下面几个:

  1. relativenumber。使用相对行号。这样就可以输入20j这样的指令快速向下移动20行,非常方便。但这种方便只限于行之间的跳转,如果要向下20行并移动到行内的某个字符,还是需要额外的操作。
  2. 使用f/F和w/W在行内进行操作。这也算方便,但不算很方便,通常要按好几下才能去到想去的位置。
    vim-easymotion插件便是改善移动速度的插件,按以下配置快捷键之后,按f就可以去到屏幕内任意一个字符,按<leader>w就能去到屏幕内任意一个单词。
map  f <Plug>(easymotion-bd-f)
nmap f <Plug>(easymotion-overwin-f)
map  <Leader>w <Plug>(easymotion-bd-w)
nmap <Leader>w <Plug>(easymotion-overwin-w)

使用额外的textobject来快速修改代码元素

对vim有深度体验的人对textobject的功能应该是印象深刻的,比如ciw能快速修改一个单词,ci)能快速修改括号里面的内容,非常方便。
但遗憾的是,内置的textobject种类太少了,比如我想修改C函数的参数,就没法修改了。现在就好了,我们装了一堆textobject,只要输入ci,就能快速修改函数参数,vif就可以快速选中整个函数的函数体,非常方便。

代码补全问题

老是会有人问我为什么不使用代码补全?在部分人眼里,代码补全就是coc、ycm和lsp,插件中没有这些就认为没有代码补全。
其实vim自带就有代码补全。
C-x C-n就能打开补全弹窗,使用C-nC-p上下选择,C-y应用补全。
是的,目前的补全功能没有lsp那么好用,我也说过为什么内核态编程难以利用lsp,使用这种补全足够了。
completion

使用podman支持多版本OS编译

podman run --it myol79:v1 /bin/bash

yum -y install make gcc kernel-devel

[root@94c6f77a7ab8 3.10.0-1160.118.1.0.1.el7.x86_64]# pwd
/usr/src/kernels/3.10.0-1160.118.1.0.1.el7.x86_64  # 记住这个路径

❯ podman container ls -a
CONTAINER ID  IMAGE                COMMAND     CREATED             STATUS                     PORTS       NAMES
94c6f77a7ab8  localhost/myol79:v1  /bin/bash   About a minute ago  Exited (0) 14 seconds ago              exciting_kirch
❯ podman container commit 94c
Getting image source signatures
Copying blob 95ad618ac3f2 skipped: already exists
Copying blob b30ea34ed42b skipped: already exists
Copying blob d81baaf4481c done   |
Copying config 4c427f1cb2 done   |
Writing manifest to image destination
4c427f1cb2d9950166ab61da0da51f0ac4a6f6f6d5c0431541dc8c6b04071c17
❯ podman image tag 4c4 myol79:v2  # 给它打个标签

修改项目Makefile,把内核源码路径修改为容器里面的路径,下面是一个例子:
obj-m := biodeadlock.o
biodeadlock-objs := biodeadlock_main.o

# KSRC = /lib/modules/`uname -r`/build
KSRC = /usr/src/kernels/3.10.0-1160.118.1.0.1.el7.x86_64

PWD = `pwd`

default:
        make -C $(KSRC) M=$(PWD) modules;

clean:
        rm *.o biodeadlock.mod.c biodeadlock.mod modules.order Module.symvers *.ko
        
然后这样编译
podman run --rm --privileged -v "$PWD":/root/myapp -w /root/myapp myol79:v2 make

常见问题

开启粘贴板支持

yum -y install vim-X11

这会安装一个vimx命令,如之前所述,这是一个enable了图形界面功能的vim版本,使用这个替换vim即可。

leaderf的报错

解决:yum -y install python-devel,重新打开vim,执行LeaderfInstallCExtension
leaderferror

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值