使用容器编译Linux内核并启动运行-续
本文的主要目的是基于Docker构建Linux内核开发环境。在上文中也提及到,我们需要安装开发套件,包括vim,zsh。这些都是Linux开发利器。本文将重点描述在Docker中如何构建这些环境。
- 网络环境配置
- vim配置
- zsh配置
- 一键式脚本实现从内核编译到内核启动全流程
网络环境配置
在对开发环境进行配置时,需要用到github的资源,因此需要对git进行加速。在启动镜像时,采用--net=host参数将容器放置在与主机同等的网络环境下。具体如下:
docker run --privileged=true --net=host -d --name release wanbo432503/ubuntu:14.04 /usr/sbin/sshd -D
vim配置
Vim是Linux下开发常用的编辑工具,由于Docker中的镜像系统为Linux 14.04,因此无法使用vimplus的配置。但好在vim-spf13也非常不错。具体配置过程如下:
sudo apt-get install vim
curl https://j.mp/spf13-vim3 -L > spf13-vim.sh && sh spf13-vim.sh
需要下载非常多的插件,如果没有进行实现的网络加速,那会非常慢。在Vim中,有几个插件值得具体介绍。
NerdTree
基本配置如下图所示。该配置的基本意思是,通过Ctrl+e打开NerdTree,使用<leader>+e查找文件,其中<leader>在spf13的配置中修改为“,”,而非默认的“\”。打开关闭文件或者目录,如果是文件的话,光标出现在打开的文件中
常用命令:
- 在多个窗口之间,可以通过Ctrl+{h,j,k,l}实现左,下,上,右窗口中的光标移动
- 和编辑文件一样,通过h j k l移动光标定位
- go 效果同上,不过光标保持在文件目录里,类似预览文件内容的功能
- i和s可以水平分割或纵向分割窗口打开文件,前面加g类似go的功能
- t 在标签页中打开
- T 在后台标签页中打开
- p 到上层目录
- P 到根目录
- K 到同目录第一个节点
- J 到同目录最后一个节点
- m 显示文件系统菜单(添加、删除、移动操作)
- ? 帮助
- q 关闭
CtrlP
用于搜索当前目录下的文件,支持模糊搜索和精确搜索。CtrlP通过ctrl+p启动,通过ctrl+d改变搜索模式
- <f5> 更新目录缓存。
- <c-f> / <c-b> 在模式之间切换
- <c-d> 在”完整路径匹配“ 和 ”文件名匹配“ 之间切换
- <c-r> 在“字符串模式” 和 “正则表达式模式” 之间切换
- <c-j> / <c-k> 上下移动光标* <c-t> 在新的 tab 打开文件
- <c-v> 垂直分割打开
- <c-x> 水平分割打开
- <c-p>, <c-n> 选择历史记录
- <c-y> 文件不存在时创建文件及目录
- <c-z> 标记/取消标记, 标记多个文件后可以使用 <c-o> 同时打开多个文件
cscope
cscope与vim结合使用,可以实现类似于sourceinsight类似的功能,非常有利于在工程里面进行代码开发。配置cscope时,主要是生成其检索所需的索引文件。在创建或更新一个工程之后,在该工程的根目录中执行如下脚本即可完成索引文件的更新。我将该脚本命名为cscmk,存放在/usr/bin中,由此可以随时通过命令cscmk更新工程的索引文件。
若出现cscope: cannot find file ...的错误,则表示此类文件为链接文件,无法被cscope识别,可以不用管。此外,在搜索过程中,若出现无法找到XX文件的情况,那说明在创建索引文件cscope.files使用了相对路径,当在其他目录打开文件进行检索时,就无法查找相应的文件。因此在生成cscope.files时,应该使用绝对路径,此处使用了`pwd`获取绝对路径
#!/bin/sh
find `pwd` -name "*.h" -o -name "*.c" -o -name "*.cc" > cscope.files
cscope -bkq -i cscope.files
ctags -R
- -R: 在生成索引文件时,搜索子目录树中的代码
- -b: 只生成索引文件,不进入cscope的界面
- -q: 生成cscope.in.out和cscope.po.out文件,加快cscope的索引速度
- -k: 在生成索引文件时,不搜索/usr/include目录
- -i: 如果保存文件列表的文件名不是cscope.files时,需要加此选项告诉cscope到哪儿去找源文件列表。可以使用“-”,表示由标准输入获得文件列表。
- -I dir: 在-I选项指出的目录中查找头文件
- -u: 扫描所有文件,重新生成交叉索引文件
- -C: 在搜索时忽略大小写
- -P path: 在以相对路径表示的文件前加上的path,这样,你不用切换到你数据库文件所在的目录也可以使用它了。
在VIM中使用cscope非常简单,首先调用“cscope add”命令添加一个cscope数据库,然后就可以调用“cscope find”命令进行查找了。VIM支持8种cscope的查询功能,如下:例如,我们想在代码中查找调用work()函数的函数,我们可以输入:“:cs find c work”,回车后发现没有找到匹配的功能,可能并没有函数调用work()。我们再输入“:cs find s work”,查找这个符号出现的位置,现在vim列出了这个符号出现的所有位置。我们还可以进行字符串查找,它会双引号或单引号括起来的内容中查找。还可以输入一个正则表达式,这类似于egrep程序的功能。
- s: 查找C语言符号,即查找函数名、宏、枚举值等出现的地方
- g: 查找函数、宏、枚举等定义的位置,类似ctags所提供的功能
- d: 查找本函数调用的函数
- c: 查找调用本函数的函数
- t: 查找指定的字符串
- e: 查找egrep模式,相当于egrep功能,但查找速度快多了
- f: 查找并打开文件,类似vim的find功能
- i: 查找包含本文件的文
若不记得cscope的功能,可以在Vim中通过cs help快速查看find的具体参数。find 的选项具体如下:
- 0或则S:查找本符号
- 1或则G:查找本定义
- 2或则D:查找本函数调用的函数
- 3或则C:查找调用本函数的函数
- 4或则T:查找本字符串
- 6或则E:查找本EGREP模式
- 7或则F:查找本文件
- 8或则I:查找包含本文件的文件
在.vimrc最后新增以下内容:
if has("cscope")
set csprg=/usr/bin/cscope
set csto=0
set cst
set nocsverb
" add any database in current directory
if filereadable("cscope.out")
cs add cscope.out
" else add database pointed to by environment
elseif $CSCOPE_DB != ""
cs add $CSCOPE_DB
endif
set csverb
map g<C-]> :cs find 3 <C-R>=expand(“<cword>”)<CR><CR>
map g<C-/> :cs find 0 <C-R>=expand(“<cword>”)<CR><CR>
endif
可通过ctrl+]或ctrl+/检索调用该函数的函数或C标识符。
zsh配置
sudo apt-get install zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
zsh配置文件在$HOME下的.zshrc中。在zsh中推荐两款非常有用的插件和一款好看的主题,配置如下:
plugins=(git extract autojump)
插件1:extract
功能强大的解压插件,所有类型的文件解压一个命令x
全搞定,再也不需要去记tar
后面到底是哪几个参数了。
sudo apt-get install extract
插件2:autojump
强大的目录自动跳转命令,会记忆你曾经进入过的目录,用模糊匹配快速进入你想要的目录,一个命令j全搞定。
sudo apt-get install autojump
主题:ys
在配置文件.zshrc中配置。
ZSH_THEME="ys"
一键式脚本实现从内核编译到内核启动全流程
用于内核开发的环境就是要简化编译启动验证的过程,避免繁琐的Linux指令打算工作计划。因此,提供一键式的脚本,完成内核编译到启动全过程。在每次修改内核后,都可以通过该脚本完成编译到启动的全过程,查看是否有错误。
值得注意的是,如果编译内核的时间过长,可以不用执行make clean以及make distclean,避免重复编译没有修改的文件内容,耽误时间。但是为了避免编译出现未知莫名的错误,每次最好还是执行make clean以及make distclean。同时,欢迎有经验的同学提供更快捷准确的内核编译方式。
#!/bin/sh
# note: Linux kernel and busybox are in the same directory
path_to_Linux_kernel=linux-4.18
path_to_busybox=busybox-1.30.0
path_to_root=`pwd`
cd $path_to_Linux_kernel
make clean
make distclean
make x86_64_defconfig
make bzImage
make modules
# obtain the bzImage
cp arch/x86/boot/bzImage $path_to_root
# create disk by type ext4
cd $path_to_root
if [ -d img ]; then
umount img
rm -rf img
fi
if [ -e disk.raw ]; then
rm disk.raw
fi
qemu-img create -f raw disk.raw 512M
mkfs -t ext4 disk.raw
mkdir img
sudo mount -o loop disk.raw img # note that, docker run --privileged=true
# install modules and init program
cd $path_to_Linux_kernel
sudo make modules_install INSTALL_MOD_PATH=../img
cd $path_to_root/$path_to_busybox
if [ -e busybox ]; then
make CONFIG_PREFIX=../img install
else
echo "Busybox is not built yet. Please build the busybox first!"
exit 2
fi
# update the init program
cd $path_to_root/img
mkdir -p etc/init.d
mkdir proc
mkdir sys
mkdir dev
touch etc/init.d/rcS
echo "#/bin/sh" > etc/init.d/rcS
echo "mount -t proc proc /proc" >> etc/init.d/rcS
echo "mount -t sysfs sysfs /sys" >> etc/init.d/rcS
chmod +777 etc/init.d/rcS
# start the bzImage
cd $path_to_root
qemu-system-x86_64 -curses -m 512M -smp 4 -kernel bzImage -drive format=raw,file=disk.raw -append "init=/linuxrc root=/dev/sda"