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

来源:互联网 发布:sql 2008 sa 密码 编辑:程序博客网 时间:2024/05/18 02:22

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

涉及的内核源码,基于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_KEXECcase LINUX_REBOOT_CMD_KEXEC:ret = kernel_kexec();break;#endif#ifdef CONFIG_HIBERNATIONcase LINUX_REBOOT_CMD_SW_SUSPEND:ret = hibernate();break;#endifdefault: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();}




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 右脸颊一直长痘怎么办 痘痘粉刺反复长怎么办 长痘留下的红印怎么办 熬夜长出的痘痘怎么办 脸上两边长痘痘怎么办 我左脸比右脸大怎么办 左脸莫名肿了怎么办 牙疼得半边脸痛怎么办 手和脸突然发麻怎么办 右半边脸麻木了怎么办 左边脸突然肿了怎么办 左半边脸皮肤疼怎么办 脸内侧的肉肿了怎么办 上火引起的脸肿怎么办 脸肿里面有硬块怎么办 内分泌失调引起的肥胖怎么办 宝宝接种证丢了怎么办 不给补办接种证怎么办 儿童接种证丢了怎么办 疫苗接种本丢了怎么办 脊灰滴剂滴多了怎么办 鞋小了挤脚趾头怎么办 大母脚趾头疼是怎么办 小脚趾内侧长茧怎么办 小脚趾肿了很痛怎么办 穿袜子大脚趾痛怎么办 脚指头长水泡很痒怎么办 走路脚打起泡了怎么办 剪完脚趾甲肿了怎么办 大脚趾关节处疼怎么办 战士10穿不进去怎么办 脚上皮肤干燥起皮怎么办 脚趾头冻了很痒怎么办 大脚趾里面有脓怎么办 大脚趾肉肿了怎么办 大脚趾边上肿了怎么办 大母脚趾关节疼怎么办 大脚趾有点歪了怎么办 大脚趾扭伤肿了怎么办 大脚趾外翻怎么办 知乎 颈椎带着胳膊疼怎么办