首页 > *nix应用编程, *nix技术, UbuntuKylin > 快速重启Linux

快速重启Linux

2013年10月7日 发表评论 阅读评论 6,407 次浏览

A rapidreboot-tool for Ubuntu based on kexec-tools and python.
https://github.com/lenky0401/rapidreboot

已经捣鼓kdump的时候,整过一个kexec的家伙,主要也就是在旧内核oops时,用它启动一个新内核,从而在新内核里执行dump工作,保存旧内核的oops信息,以便问题排查。
旧内核里直接启动新内核,这可以跳过机器硬件自检,从而节约一些开机时间,因此kexec的新用法就是快速重启Linux,以我当前所用的Ubuntu为例。

首先需要内核打开对应的选项,Ubuntu已经打开了:
lenky@Ubuntu:~$ cat /boot/config-`uname -r` | grep KEXEC
CONFIG_KEXEC=y
CONFIG_KEXEC_JUMP=y

其次,需要应用层工具kexec-tools,默认没有安装,Ubuntu里需要执行如下命令进行安装:
lenky@Ubuntu:~$ sudo apt-get install kexec-tools

有了上面两个支持,做快速重启的步骤就是两步:
1,加载内核:
lenky@Ubuntu:~$ sudo kexec -l /boot/vmlinuz-`uname -r` –initrd=/boot/initrd.img-`uname -r` –append=”`cat /proc/cmdline`”

2,执行重启:
lenky@Ubuntu:~$ sudo kexec -e

当然,如果每次快速重启都需要自己去敲上面这两个命令,未免太麻烦了点。不过别当心,kexec工具包已经替我们做了相关工作,只要我们打开对应的选项,那么在进行任意普通的重启操作(不管是命令行执行reboot或是gnome界面里点击重启按钮)时,实际执行的都是快速重启。
这个选项就是配置文件/etc/default/kexec里的“LOAD_KEXEC=true”:

lenky@Ubuntu:~$ cat /etc/default/kexec
# Defaults for kexec initscript
# sourced by /etc/init.d/kexec and /etc/init.d/kexec-load

# Load a kexec kernel (true/false)
LOAD_KEXEC=true

# Kernel and initrd image
KERNEL_IMAGE="/vmlinuz"
INITRD="/initrd.img"

# If empty, use current /proc/cmdline
APPEND=""

# Load the default kernel from grub config (true/false)
USE_GRUB_CONFIG=false

快速重启也有个麻烦点,那就是你点重启无法经过grub了,那么也就无法选择进入其他操作系统(如果你有装的话)了,你可以将其改为“LOAD_KEXEC=false”来进行关闭。

下面解释一下LOAD_KEXEC选项是如何作用的。这涉及到另外两个脚本:

lenky@Ubuntu:~$ ls /etc/rc6.d/*kexec* -l
lrwxrwxrwx 1 root root 20  9月 24 17:13 /etc/rc6.d/K18kexec-load -> ../init.d/kexec-load
lrwxrwxrwx 1 root root 15  9月 24 17:13 /etc/rc6.d/S85kexec -> ../init.d/kexec
lenky@Ubuntu:~$ cat /etc/rc6.d/K18kexec-load
#! /bin/sh
### BEGIN INIT INFO
# Provides:		kexec-load
# Required-Start:
# Required-Stop:	$local_fs $remote_fs kexec
# Should-Stop:		autofs
# Default-Start:
# Default-Stop:		6
# Short-Description: Load kernel image with kexec
# Description:
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin
NOKEXECFILE=/tmp/no-kexec-reboot

. /lib/lsb/init-functions

test -r /etc/default/kexec && . /etc/default/kexec

process_grub_entry() {
	initrd_image=
	while read command args; do
		if [ "$command" = "linux" ]; then
			echo "$args" | while read kernel append; do
			echo KERNEL_IMAGE=\"${kernel}\"
			echo APPEND=\"${append}\"
			done
		elif [ "$command" = "initrd" ]; then
			initrd_image=${args}
		fi
	done
	echo INITRD=\"$initrd_image\"
}

get_grub_kernel() {
	test -f /boot/grub/grub.cfg || return
	data=$(cat /boot/grub/grub.cfg)

	default=$(echo "$data" | awk '/^set default/ {print $2}' | cut -d'"' -f2)
	if [ -z "$default" ]; then
		default=0
	fi
	start_offset=$((default + 1))
	end_offset=$((default + 2))

	# grub entries start with "menuentry" commands.  Get the line 
	# numbers that surround the first entry
	offsets=$(echo "$data" | grep -n ^menuentry | cut -d: -f1)
	begin=$(echo "$offsets" | tail -n+$start_offset | head -n1)
	end=$(echo "$offsets" | tail -n+$end_offset | head -n1)

	# If this is the last entry, we need to read to the end of the file
	# or to the end of boot entry section
	if [ -z "$end" ]; then
		numlines=$(echo "$data" | tail --lines=+$begin | grep -n "^### END" | head -1 | cut -d: -f1)
		end=$((begin + numlines - 1))
	fi

	length=$((end - begin))
	entry=$(echo "$data" | tail -n+$begin | head -n$length)
	eval $(echo "$entry" | process_grub_entry)
}

do_stop () {
	test "$LOAD_KEXEC" = "true" || exit 0
	test -x /sbin/kexec || exit 0
	test "x`cat /sys/kernel/kexec_loaded`y" = "x1y" && exit 0

	if [ -f $NOKEXECFILE ]
	then
		/bin/rm -f $NOKEXECFILE
		exit 0
	fi

	test "$USE_GRUB_CONFIG" = "true" && get_grub_kernel

	REAL_APPEND="$APPEND"

	test -z "$REAL_APPEND" && REAL_APPEND="`cat /proc/cmdline`"
	log_action_begin_msg "Loading new kernel image into memory"
	if [ -z "$INITRD" ]
	then
		kexec -l "$KERNEL_IMAGE" --append="$REAL_APPEND"
	else
		kexec -l "$KERNEL_IMAGE" --initrd="$INITRD" --append="$REAL_APPEND"
	fi
	log_action_end_msg $?
}

case "$1" in
  start)
	# No-op
	;;
  restart|reload|force-reload)
	echo "Error: argument '$1' not supported" >&2
	exit 3
	;;
  stop)
	do_stop
	;;
  *)
	echo "Usage: $0 start|stop" >&2
	exit 3
	;;
esac
exit 0
lenky@Ubuntu:~$ cat /etc/rc6.d/S85kexec
#! /bin/sh
### BEGIN INIT INFO
# Provides:		kexec
# Required-Start:	
# Required-Stop:   	reboot
# X-Stop-After:		umountroot
# Default-Start:
# Default-Stop:		6
# Short-Description: Execute the kexec -e command to reboot system
# Description:
### END INIT INFO

PATH=/sbin:/bin

. /lib/lsb/init-functions

test -r /etc/default/kexec && . /etc/default/kexec

do_stop () {
	test "x`cat /sys/kernel/kexec_loaded`y" = "x1y" || exit 0
	test -x /sbin/kexec || exit 0

	log_action_msg "Will now restart with kexec"
        kexec -e
        log_failure_msg "kexec failed"
}

case "$1" in
  start)
	# No-op
	;;
  restart|reload|force-reload)
	echo "Error: argument '$1' not supported" >&2
	exit 3
	;;
  stop)
	do_stop
	;;
  *)
	echo "Usage: $0 start|stop" >&2
	exit 3
	;;
esac
exit 0

在进行runlevel切换时,Linux就会自动执行对应的脚本,对于runlevel 6有一点特殊,它的K开头或S开头的脚本都会带上stop参数。在脚本K18kexec-load里,选项LOAD_KEXEC用来控制是否要加载新内核,为true则加载,否则直接exit退出。在脚本S85kexec里,通过读取/sys/kernel/kexec_loaded判断是否已经加载新内核,如果加载了则启动新内核,进行快速重启,否则走原有重启流程。这样,最终的结果就是:LOAD_KEXEC选项控制了是否启动快速重启。

转载请保留地址:http://www.lenky.info/archives/2013/10/2351http://lenky.info/?p=2351


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.