05
2017
10

Linux电源管理(二)系统reboot/shutdown过程

概述

计算机包括嵌入式设备,关机和重启是常见的两个操作。本文主要主要描述这两个动作背后和流程和实现。

下机我们先看一下,linux支持的类似关机重启这样的命令的其它相关命令。

/*
 * Commands accepted by the _reboot() system call.
 *
 * RESTART     Restart system using default command and mode.
 * HALT        Stop OS and give system control to ROM monitor, if any.
 * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
 * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
 * POWER_OFF   Stop OS and remove all power from system, if possible.
 * RESTART2    Restart system using given command string.
 * SW_SUSPEND  Suspend system using software suspend if compiled in.
 * KEXEC       Restart system using a previously loaded Linux kernel
 */

#define LINUX_REBOOT_CMD_RESTART 0x01234567 //重启命令
#define LINUX_REBOOT_CMD_HALT 0xCDEF0123 
#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC//关机命令
#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
#define LINUX_REBOOT_CMD_KEXEC 0x45584543

reboot/shutdown流程

在Linux操作系统中,可以通过reboot、halt、poweroff等命令,发起reboot,具体的操作流程如下:
这里写图片描述

  • 一般的Linux操作系统,在用户空间都提供了一些工具集合(如常在嵌入式系统使用的Busybox),这些工具集合包含了reboot、halt和poweroff三个和Reboot相关的命令。读者可以参考man帮助文档,了解这些命令的解释和使用说明
  • 用户空间程序通过reboot系统调用,进入内核空间
  • 内核空间根据执行路径的不同,提供了kernel_restart、kernel_halt和kernel_power_off三个处理函数,响应用空间的reboot请求
  • 这三个处理函数的处理流程大致相同,主要包括:向关心reboot过程的进程发送Notify事件;调用drivers核心模块提供的接口,关闭所有的外部设备;调用drivers syscore模块提供的接口,关闭system core;调用Architecture相关的处理函数,进行后续的处理;最后,调用machine相关的接口,实现真正意义 Reboot 
  • 另外,借助TTY模块提供的Sysreq机制,内核提供了其它途径的关机方法,如某些按键组合、向/proc文件写入命令等。

实现流程分析

系统调用入口

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;
}

重启函数入口

void kernel_restart(char *cmd)
{
    kernel_restart_prepare(cmd);  //通知链通知,关闭所有设备(bus,driver的shutdown接口)
    migrate_to_reboot_cpu();//
    syscore_shutdown(); //系统核心器件关闭(例如中断等)
    if (!cmd)
        printk(KERN_EMERG "Restarting system.\n");
    else
        printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
    kmsg_dump(KMSG_DUMP_RESTART); //向世界发出最后的声音
    machine_restart(cmd); //由machine-core的代码,接管后续的处理
}

关机接口

void kernel_power_off(void)
{
    kernel_shutdown_prepare(SYSTEM_POWER_OFF);
    if (pm_power_off_prepare)
        pm_power_off_prepare();
    migrate_to_reboot_cpu();
    syscore_shutdown();
    printk(KERN_EMERG "Power down.\n");
    kmsg_dump(KMSG_DUMP_POWEROFF);
    machine_power_off();
}

驱动需要实现什么?

实现各自的shutdown接口,以正确关闭对应的设备

参考资料

<1>.http://www.wowotech.net/pm_subsystem/reboot.html

上一篇:Web应用程序的最佳实践 下一篇:Linux电源管理(三)电源管理接口