背景
Kpatch是给Linux内核打热补丁的工具,所谓热补丁,就是指打完补丁后,补丁可立即生效,而不需要像传统打补丁那样必须重启Linux才能生效。
Kpatch简介
最早出现的打热补丁工具不是Kpatch,而是Ksplice。但是Ksplice被Oracle收购后,一些发行版生产商就不得不开发自己的热补丁工具,分别是Redhat的Kpatch和Suse的KGraft。同时,在这两家厂商的推进下,kernel4.0开始,开始集成了livepatch技术,该技术和其他热补丁技术类似。
Kpatch虽然是Redhat研发,但其也支持Ubuntu、Debian、Oracle Linux等的发行版,本文将介绍在Ubuntu14.04下安装和使用Kpatch软件。
不足
热补丁不是万能的,尤其是现在技术还不足够成熟的时候。在使用Kpatch热补丁前,我们需要知道Kpatch的不足之处:
1.如果在已经打过patch的环境继续打patch,那么新patch必须是基于上个patch打的补丁。
2.不支持修改数据结构的patch,如果有这样的patch,用户必须改代码。
3.不支持修改__init函数的补丁。
4.不支持修改静态数据。
5.不支持修改vdso的函数,因为其运行在用户空间。
准备
注意:必须保证有15GB的空闲磁盘空间,以便在~/.kpatch下生成中间缓存文件。
1.安装编译Kpatch所需的依赖环境:
apt-get install make gcc libelf-dev
安装kptch-build运行所需的环境:
apt-get install dpkg-dev
apt-get build-dep linux
# optional, but highly recommended
apt-get install ccache
ccache --max-size=5G
安装内核调试信息:
# Add ddebs repository
codename=$(lsb_release -sc)
sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse
EOF
# add APT key
wget -Nq http://ddebs.ubuntu.com/dbgsym-release-key.asc -O- | sudo apt-key add -
apt-get update && apt-get install linux-image-$(uname -r)-dbgsym
安装Kpatch
从github下载源码,路径为:https://github.com/dynup/kpatch。
这里我没有使用master,而是用了当前最新的branch——v0.3.4。安装较为简单:
make
make install
注意:如果使用了新的内核,那么也需要重新编译并安装Kpatch,因为它会根据当前kernel版本,生成一些符号表文件。如果没有重新编译安装Kpatch,那么你在使用Kpatch-build时,就会出现“unable to find Module.symvers for kpatch core module.”的错误。
使用Kpatch
Kpatch安装完成后,提供了kpath和kpatch-build这两个脚本程序。其中,kpatch-build通过源代码、patch文件、config文件等生成一个或多个ko模块,kpatch再加载这些ko模块,从而达到打热补丁的效果。
准备内核源代码
从http://www.kernel.org或者其他镜像源下载3.9.0及以上的kernel版本(Kpatch不支持3.9以下的版本),尽量选择国内的镜像,下载速度会稍微快一些。我这里使用3.9版本的kernel代码。
cd /usr/src
wget http://mirror.lzu.edu.cn/kernel/v3.x/linux-3.9.tar.gz
tar zxvf linux-3.9.tar.gz
cd linux-3.9
# 生成.config文件,为了编译较快,我这里只使用较精简的config
make localmodconfig
# 编译并安装新版本的kernel
# 如果编译过程中出现什么问题,请参考其他文章解决,这里不再详述
make bzImage -j 4 && make modules -j 4 && make modules_install && make install
# 配置grub,使最新编译的kernel生效
reboot
生成热补丁
重启后,再进入/usr/src目录,做一份代码的拷贝,并在拷贝代码中,在Kpatch允许的范围内,修改一些代码,然后生成patch文件。我这里是在创建文件夹的地方增加了一行打印,调用的地方不会太多,而且容易触发,查看效果比较直观。
cd /usr/src
cp -arf linux-3.9 linux-3.9-modify
vim linux-3.9-modify/fs/namei.c
# 查找vfs_mkdir关键字,并在vfs_mkdir函数中的字段定义后,直接加入一些打印,我这里加入了“printk(KERN_ERR "kpatch test mkdir\n");”创建文件夹后,就会打印kpatch test mkdir。
# 创建patch文件
diff -Naur linux-3.9 linux-3.9-modify > my.patch
之后,就可通过kpatch-build生成ko模块。
kpatch-build -s /usr/src/linux-3.9 /usr/src/my.patch
上面命令行中,-s后跟kernel源代码的目录。-r后跟指定的vmlinux,若不直接指定vmlinux,kpatch就会从源目录中查找vmlinux,如上所示中,我们就没有指定vmlinux。patch文件至于最后的位置。kpatch-build更多的参数可通过命令kpatch-build –help或者man kpatch-build来查看。
打入热补丁
kpatch-build如果运行失败,可通过日志文件~/.kpatch/build.log来查看失败的具体原因。如果运行成功,则会在当前目录生成ko文件。因为我的patch名为my.patch,根据其名称生成规则,就生成了名为kpatch-my.ko的模块文件。注意,kpatch-build生成的ko文件,不能通过insmod的方式来装载,而必须使用kpatch load来装载。并通过kpatch unload来卸载,同时,也可通过kpatch list来查看加载信息。
# kpatch load kpatch-my.ko
loading patch module: kpatch-my.ko
# kpatch list
Loaded patch modules:
kpatch_my
Installed patch modules:
加载成功后,再任意创建一个目录,查看/var/log/kern.log或者dmesg,相关日志如下:
# dmesg
[31478.707301] kpatch: loaded patch module 'kpatch_my'
[31839.683442] kpatch test mkdir
期望的打印出现,证明我们这次打热补丁完美成功。
apt-get安装kpatch
如果觉得源码安装kpatch麻烦,可直接使用apt-get安装,解决所以依赖问题。当然,这种方法不能选择自己想要的版本,而且版本偏老。
apt-get install kpatch
apt-get install kpatch-build