使用容器编译Linux内核并启动运行-续

本文的主要目的是基于Docker构建Linux内核开发环境。在上文中也提及到,我们需要安装开发套件,包括vim,zsh。这些都是Linux开发利器。本文将重点描述在Docker中如何构建这些环境。

  1. 网络环境配置
  2. vim配置
  3. zsh配置
  4. 一键式脚本实现从内核编译到内核启动全流程

网络环境配置

在对开发环境进行配置时,需要用到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的配置中修改为“,”,而非默认的“\”。打开关闭文件或者目录,如果是文件的话,光标出现在打开的文件中

使用容器编译Linux内核并启动运行-续

常用命令:

  • 在多个窗口之间,可以通过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"