本文发自 http://www.binss.me/blog/how-to-compile-and-install-linux-kernel/,转载请注明出处。

最近在折腾KVM,频繁更换kernel,在此记录下对kernel进行编译和安装的过程:

环境是Ubuntu 16.04 LTS。

在编译之前先说说遇到的几个坑:

  1. 不要在Mac下clone源码

    举个栗子,Mac下clone的版本:

    ubuntu@ubuntu-xenial:/vagrant/KVM-Learning/include/uapi/linux/netfilter_ipv4$ ll
    total 36
    drwxr-xr-x 1 ubuntu ubuntu   340 Dec  2 09:39 ./
    drwxr-xr-x 1 ubuntu ubuntu 16320 Dec  3 13:30 ../
    -rw-r--r-- 1 ubuntu ubuntu  6659 Dec  2 09:39 ip_tables.h
    -rw-r--r-- 1 ubuntu ubuntu   362 Dec  2 09:39 ipt_ah.h
    -rw-r--r-- 1 ubuntu ubuntu   758 Dec  2 09:39 ipt_CLUSTERIP.h
    -rw-r--r-- 1 ubuntu ubuntu   368 Dec  2 09:39 ipt_ecn.h
    -rw-r--r-- 1 ubuntu ubuntu   659 Dec  2 09:39 ipt_LOG.h
    -rw-r--r-- 1 ubuntu ubuntu   405 Dec  2 09:39 ipt_REJECT.h
    -rw-r--r-- 1 ubuntu ubuntu   368 Dec  2 09:39 ipt_ttl.h
    -rw-r--r-- 1 ubuntu ubuntu   234 Dec  2 09:39 Kbuild

    Linux下clone的版本:

    ubuntu@ubuntu-xenial:~/KVM-Learning/include/uapi/linux/netfilter_ipv4$ ll
    total 68
    drwxrwxr-x  2 ubuntu ubuntu  4096 Dec  3 13:52 ./
    drwxrwxr-x 27 ubuntu ubuntu 20480 Dec  3 13:52 ../
    -rw-rw-r--  1 ubuntu ubuntu  6659 Dec  3 13:52 ip_tables.h
    -rw-rw-r--  1 ubuntu ubuntu   362 Dec  3 13:52 ipt_ah.h
    -rw-rw-r--  1 ubuntu ubuntu   758 Dec  3 13:52 ipt_CLUSTERIP.h
    -rw-rw-r--  1 ubuntu ubuntu   368 Dec  3 13:52 ipt_ecn.h
    -rw-rw-r--  1 ubuntu ubuntu   838 Dec  3 13:52 ipt_ECN.h
    -rw-rw-r--  1 ubuntu ubuntu   659 Dec  3 13:52 ipt_LOG.h
    -rw-rw-r--  1 ubuntu ubuntu   405 Dec  3 13:52 ipt_REJECT.h
    -rw-rw-r--  1 ubuntu ubuntu   368 Dec  3 13:52 ipt_ttl.h
    -rw-rw-r--  1 ubuntu ubuntu   312 Dec  3 13:52 ipt_TTL.h
    -rw-rw-r--  1 ubuntu ubuntu   234 Dec  3 13:52 Kbuild

    可以发现Mac下clone的版本只有ipt_ecn.h,而Liunx下clone的版本有在ipt_ecn.h和ipt_ECN.h。为什么呢?因为Mac OS不区别文件名大小写,因此在ipt_ecn.h和ipt_ECN.h中只保留了ipt_ecn.h一个文件,因此在编译时自然就因为缺文件而出错了:

    cc1: some warnings being treated as errors
    scripts/Makefile.build:295: recipe for target 'net/ipv4/netfilter/ipt_ECN.o' failed
    make[3]: *** [net/ipv4/netfilter/ipt_ECN.o] Error 1
    scripts/Makefile.build:440: recipe for target 'net/ipv4/netfilter' failed
    make[2]: *** [net/ipv4/netfilter] Error 2
    scripts/Makefile.build:440: recipe for target 'net/ipv4' failed
    make[1]: *** [net/ipv4] Error 2
    Makefile:968: recipe for target 'net' failed
    make: *** [net] Error 2
  2. clone中断

    由于Linux kernel代码巨达2G,而git clone又不支持断点续传,再加上墙的干扰,如果能一次成功clone完,只能说你的运气不错。

    如果你像我一样运气不太好,建议使用两阶段clone法,首先clone最后一个commit,成功后再把历史fetch下来:

    $ git clone --depth=1 
    $ git fetch --unshallow

    如果你比我还背,建议使用海外VPS将代码clone下来,然后zip后下载到本地。

  3. 编译前确保硬盘有足够的空间

    编译后du了文件夹,足足有15G。如果在编译的过程中硬盘满了,就会抛出奇奇怪怪的错误。由于我用的是VitualBox虚拟机,因此最好的建立时就把磁盘扩充到50G:

    在VirtualBox中右键虚拟机,Show in Finder,可以打开虚拟机的镜像文件夹,cd到该目录,执行以下命令:

    VBoxManage clonehd ubuntu-xenial-16.04-cloudimg.vmdk ubuntu-xenial-16.04-cloudimg.vdi --format vdi
    VBoxManage modifyhd ubuntu-xenial-16.04-cloudimg.vdi --resize 51200

    在VirtualBox中选中虚拟机,Settings-Storage,选中SCSI Port 0,点击右边按钮,Choose Virtual Hard Disk File,选中刚刚新创建的vdi镜像。

    启动虚拟机,df可以发现硬盘已被扩充到50G。此后可以删除原来的镜像文件ubuntu-xenial-16.04-cloudimg.vmdk。

基本方法

编译和安装

  1. 下载源代码

    将代码clone下来。

    可以选择linus在github维护的最新版:https://github.com/torvalds/linux.git

    由于希望能够选择一个稳定的版本,因此选择了从stable分支中的4.8.10版本作为编译和研究的对象:GitHub - GiantVM/KVM-Learning

    git clone https://github.com/GiantVM/KVM-Learning
  2. 编译环境

    我选择Ubuntu 16.04。

    安装必要的软件包:

    sudo apt-get install build-essential libssl-dev ncurses-dev xz-utils kernel-package
  3. 配置

    通过make config一步步配置kernel参数,或者选择偷懒,直接拷贝当前kernel的配置:

    cp /boot/config-4.4.0-51-generic .config
    make menuconfig

    或者更加方便:

    make localmodconfig
  4. 编译

    make

    为了提升编译速度,如果有多核处理器,建议使用-j参数增加并行编译的任务数。比如开48个核,只需两分钟就能编译完哟。

    make -j 48
  5. 安装

    安装模块

    make modules_install

    安装内核

    make install

    为了避免神秘panic,来到/boot下找到新安装的kernel名称,然后手动update-initramfs:

    update-initramfs -c -k 4.8.10+
    update-grub2

单独编译模块

这里以编译kvm为例:

  1. 编译

    make clean CONFIG_KVM=m CONFIG_INTEL_KVM=m -C /home/binss/Desktop/KVM-Learning M=/home/binss/Desktop/KVM-Learning/arch/x86/kvm
    make CONFIG_KVM=m CONFIG_INTEL_KVM=m -C /home/binss/Desktop/KVM-Learning M=/home/binss/Desktop/KVM-Learning/arch/x86/kvm -j 4
  2. 复制 在/home/binss/work/KVM-Learning/arch/x86/kvm将生成kvm.ko和kvm-intel.ko两个模块,将其复制到/lib/modules/$(uname -r)/kernel/arch/x86/kvm目录下。

    cp kvm.ko /lib/modules/$(uname -r)/kernel/arch/x86/kvm
    cp kvm-intel.ko /lib/modules/$(uname -r)/kernel/arch/x86/kvm
  3. 安装

    depmod -a

    重新配置模块依赖关系。

    modprobe kvm
    modprobe kvm_intel

    加载两个模块。

或直接通过make modules:

make modules SUBDIRS=/home/binss/Desktop/KVM-Learning/arch/x86/kvm
make modules_install SUBDIRS=/home/binss/Desktop/KVM-Learning/arch/x86/kvm

移除

make install一时爽,移除就比较麻烦了,需要手动删除以下目录的文件(以kernel 4.8.10为例),需要root权限:

rm /boot/vmlinuz-4.8.10
rm /boot/initrd.img-4.8.10
rm /boot/System.map-4.8.10
rm /boot/config-4.8.10
rm -rf /lib/modules/4.8.10/
rm /var/lib/initramfs-tools/4.8.10/

删除完后更新grub:

update-grub2

Debian大法

使用前面的方法编译并安装kernel并reboot后,我悲剧地panic了。只能切回原来的kernel进入系统。

2017.04.17 已解决并更新文章,请放心使用以上方法

由于我的环境是Ubuntu,所以考虑使用debian大法,即使用make-kpkg来编译kernel,它是debian系提供的一套编译并打包kernel的脚本,见:

https://debian-handbook.info/browse/squeeze/sect.kernel-compilation.html

优点

  • 使用fakeroot编译,无需放到/usr/src/linux/下以root权限进行编译。
  • 稳定,基本不会有panic的悲剧发生。
  • 方便,通过dpkg进行安装和管理,删除只需一行命令。

编译和安装

  1. 配置

    fakeroot make-kpkg --config
  2. 清理

    如果之前编译过,这次想编译版本号不同的kernel,建议先清理下:

    fakeroot make-kpkg clean
  3. 编译

    fakeroot make-kpkg --append-to-version=test kernel_image kernel_headers --initrd -j 48
    • kernel-image 生成kernel和modules。
    • kernel-doc 生成kernel相应的文档。
    • kernel-headers 生成kernel的header files。
    • kernel-source 生成kernel相应的源代码。

    参数

    • --config 手动指定配置文件
    • --initrd 生成initramfs
    • --revision 指定版本号。需要以数字开头,即上面的1.0.0BINSS和1。
    • -j 编译路数,CPU core越多开越高速度越快。
    • --append-to-version 为原kernel新增后缀。如--append-to-version -kvm由linux-image-4.8.10生成linux-image-4.8.10-kvm:
      ii  linux-image-4.8.10                         1.0.BINSS                                                   amd64        Linux kernel binary image for version 4.8.10
      ii  linux-image-4.8.10-kvm                     1                                                           amd64        Linux kernel binary image for version 4.8.10-kvm
  4. 安装

    编译完成后,在上级目录生成linux-image-4.8.10_1.0.BINSS_amd64.deb和linux-headers-4.8.10_1.0.BINSS_amd64.deb,安装即可:

    sudo dpkg -i linux-headers-4.8.10_1.0.BINSS_amd64.deb
    sudo dpkg -i linux-image-4.8.10_1.0.BINSS_amd64.deb

打patch

fakeroot make-kpkg clean
zcat /usr/src/kernel-patches/diffs/grsecurity2/grsecurity-2.1.14-2.6.32.13-201005151340.patch.gz | patch -p1
fakeroot make-kpkg --append-to-version -grsec --revision 1 --initrd kernel-image

移除

先看看装了哪些kernel包:

dpkg --list | grep linux-image

卸载之

sudo apt-get purge linux-image-4.8.10

也可以通过GUI工具Synaptic进行卸载。

卸载后更新grub:

update-grub2

直接下载安装

当然,如果对kernel没有什么自定义配置,使用别人编译好的kernel是最吼的:

到这个网站下载需要的kernel: http://kernel.ubuntu.com/~kernel-ppa/mainline

wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9.2/linux-headers-4.9.2-040902_4.9.2-040902.201701090331_all.deb
wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9.2/linux-headers-4.9.2-040902-generic_4.9.2-040902.201701090331_amd64.deb
wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9.2/linux-image-4.9.2-040902-generic_4.9.2-040902.201701090331_amd64.deb

安装:

sudo dpkg -i *.deb