Linux关机重启停机(shutdown reboot halt)流程分析

由于本人水平相当有限,不当之处还望大家多多指教。

涉及的内核源码,基于linux-3.10.102。

首先,对于用户态发起的这类操作请求,最终都是通过sys_reboot系统调用(源码在kernel/sys.c)实现的。

其代码如下。如其注释所言,他除了可以重启关机停机,还可以修改ctrl-alt-del组合键的含义。

另外,注释还说到此系统调用不会做sync。即sync需要用户在调用此系统调用之前自己完成。

/*
 * Reboot system call: for obvious reasons only root may call it,
 * and even root needs to set up some magic numbers in the registers
 * so that some mistake won't make this reboot the whole machine.
 * You can also set the meaning of the ctrl-alt-del-key here.
 *
 * reboot doesn't sync: do that yourself before calling this.
 */
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(current);
	char buffer[256];
	int ret = 0;

	/* We only trust the superuser with rebooting the system. */
	if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
		return -EPERM;

	/* For safety, we require "magic" arguments. */
	if (magic1 != LINUX_REBOOT_MAGIC1 ||
	    (magic2 != LINUX_REBOOT_MAGIC2 &&
	                magic2 != LINUX_REBOOT_MAGIC2A &&
			magic2 != LINUX_REBOOT_MAGIC2B &&
	                magic2 != LINUX_REBOOT_MAGIC2C))
		return -EINVAL;

	/*
	 * If pid namespaces are enabled and the current task is in a child
	 * pid_namespace, the command is handled by reboot_pid_ns() which will
	 * call do_exit().
	 */
	ret = reboot_pid_ns(pid_ns, cmd);
	if (ret)
		return ret;

	/* Instead of trying to make the power_off code look like
	 * halt when pm_power_off is not set do it the easy way.
	 */
	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
		cmd = LINUX_REBOOT_CMD_HALT;

	mutex_lock(&reboot_mutex);
	switch (cmd) {
	case LINUX_REBOOT_CMD_RESTART:
		kernel_restart(NULL);
		break;

	case LINUX_REBOOT_CMD_CAD_ON:
		C_A_D = 1;
		break;

	case LINUX_REBOOT_CMD_CAD_OFF:
		C_A_D = 0;
		break;

	case LINUX_REBOOT_CMD_HALT:
		kernel_halt();
		do_exit(0);
		panic("cannot halt");

	case LINUX_REBOOT_CMD_POWER_OFF:
		kernel_power_off();
		do_exit(0);
		break;

	case LINUX_REBOOT_CMD_RESTART2:
		if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
			ret = -EFAULT;
			break;
		}
		buffer[sizeof(buffer) - 1] = '\0';

		kernel_restart(buffer);
		break;

#ifdef CONFIG_KEXEC
	case LINUX_REBOOT_CMD_KEXEC:
		ret = kernel_kexec();
		break;
#endif

#ifdef CONFIG_HIBERNATION
	case LINUX_REBOOT_CMD_SW_SUSPEND:
		ret = hibernate();
		break;
#endif

	default:
		ret = -EINVAL;
		break;
	}
	mutex_unlock(&reboot_mutex);
	return ret;
}

从上述代码来看,相关的操作分别是由函数kernel_restart, kernel_power_off, kernel_halt完成的。

另外,对于修改ctrl-alt-del组合键含义的操作(LINUX_REBOOT_CMD_CAD_ON和LINUX_REBOOT_CMD_CAD_OFF),

内核只是简单修改一下变量C_A_D的值,然后就返回了。

kernel_restart, kernel_power_off, kernel_halt这几个函数的实现也很相似:

先是通知reboot_notifier_list中的模块,系统要重启了。

然后将调用此系统调用的线程迁移到CPU 0上面。

然后执行syscore_ops_list中的各种关机操作。

最后,将系统转入restart/halt/power_off流程。


再看看用户态触发sys_reboot系统调用的C库函数reboot(man 2 reboot)的说明,对于重启关机停机类的操作,其中全都提到If not preceded by a sync(2), data will be lost.。可见,如果在调用reboot库函数前,没有调用sync(2),会导致未保存的文件数据丢失。

同时,说明中还提到对于成功的关机重启停止操作,reboot函数将不会返回。

通常,比较完善的关机重启停机流程,是通过用户态工具reboot实现的。

如果查看此工具的帮助,会发现一共有3个工具,分别是reboot, halt, poweroff。

但他们实际上对应于同一个可执行程序/sbin/reboot。

[root@localhost ~]# ls -l /sbin/reboot 
-rwxr-xr-x. 1 root root 13932 Jun 25  2013 /sbin/reboot
[root@localhost ~]# ls -l /sbin/halt 
lrwxrwxrwx. 1 root root 6 Aug  7  2016 /sbin/halt -> reboot
[root@localhost ~]# ls -l /sbin/poweroff 
lrwxrwxrwx. 1 root root 6 Aug  7  2016 /sbin/poweroff -> reboot

这三个工具,分别实现他们的名字对应的功能。

如果使用时,加了--force选项,则他们将直接调用C库函数reboot。这会导致未保存的文件数据丢失。

如果未加--force,则他们会执行用户态工具shutdown。由此可见,完善的操作流程是由shutdown程序完成的。

shutdown具体是如何组织流程的,可以参考其手册页 (man 8  shutdown)。

实际上,主要工作是shutdown向init进程发起了一个改变系统运行级别(runlevel)的请求,然后由init进程将系统降到相应的运行级别。


最后,感觉sys_reboot似乎并没有对可能存在着的大量的用户态进程进行妥善处理。而且他的注释也说明了,需要用户自己调用sync保存数据。

sys_reboot只管重启关机停机。

因此,完善的结束用户态的所有上下文的任务,就留给了用户态自己。而从前面的分析来看,正常的流程,最终就是init进程负责清空所有的用户态上下文。

这样一来,内核与用户态的关系就极其简单清晰。内核就是以系统调用为界面,为用户提供服务。用户空间的各种复杂关系,是用户空间自己的事。

系统启动时,内核在后期,拉起init进程。然后init进程通过各种系统调用,建立起用户态的一切。

最后系统正常关闭时,同样由init进程通过各种系统调用,清空用户态的一切,最后通过sys_reboot关闭系统。

当然,对于非正常关机,例如某个进程直接触发了sys_reboot,那么用户态的上下文就没有妥善结束,因此就可能丢失未保存的数据。


最后,再额外提一下,内核自身(例如,软件看门狗)在紧急情况下,想重启系统,可能会通过直接调用emergency_restart函数来实现。

下面是其代码

/**
 *	emergency_restart - reboot the system
 *
 *	Without shutting down any hardware or taking any locks
 *	reboot the system.  This is called when we know we are in
 *	trouble so this is our best effort to reboot.  This is
 *	safe to call in interrupt context.
 */
void emergency_restart(void)
{
	kmsg_dump(KMSG_DUMP_EMERG);
	machine_emergency_restart();
}
EXPORT_SYMBOL_GPL(emergency_restart);

void machine_emergency_restart(void)
{
	__machine_emergency_restart(1);
}

static void __machine_emergency_restart(int emergency)
{
	reboot_emergency = emergency;
	machine_ops.emergency_restart();
}




  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值