Android shutdown/reboot流程


基于Android-9 + msm-4.4

一、简介

当执行 adb reboot XXX 或 adb reboot -p 时会分别走 reboot 流程和 shutdown 流程。


二、用户代码流程

1. 相关文件

/system/core/init/init.cpp //用户空间reboot实现逻辑文件
/system/core/init/reboot.cpp
/system/core/init/property_service.cpp

/system/core/reboot/reboot.c  //提供reboot命令行工具
/bionic/libc/bionic/reboot.cpp //提供reboot()库函数

init.cpp 的一个编译产物是 /init


2. reboot执行流程

reboot //命令, 在 reboot.c 中实现,只是写了一个属性
    snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg); //cmd为"reboot"或"shutdown",optarg默认为shell
    property_set(ANDROID_RB_PROPERTY , property_val); //reboot.c 只是设置临时属性 "sys.powerctl"  #######
        PropertySet(name, value, error) //property_service.cpp
            property_changed(name, value) //init.cpp
                if (name == "sys.powerctl") {
                    shutdown_command = value;
                    do_shutdown = true;
                }

若是直接执行 reboot 命令的话,传给 sys.powerctl 属性的就是 "reboot,shell"; 若是直接执行 reboot -p 命令的话,传给 sys.powerctl 属性的就是 "shutdown,shell"; 若是直接执行 reboot -p HelloWorld 命令的话,传给 sys.powerctl 属性的就是 'shutdown,HelloWorld';


执行完这个后会唤醒init的主线程,然后会执行 HandlePowerctlMessage(),先看下其调用位置,调用位置在init主线程的死循环中:

kernel_init //init/main.c 内核init线程
    run_init_process(execute_command); //proc/cmdline中"init=/init", 只包含一个init.cpp文件
        main //init.cpp init进程的主函数,参数通过"Booting kernel"传入,应该没参数
             while (true) {
                if (do_shutdown && !shutting_down) {
                    do_shutdown = false;
                    if (HandlePowerctlMessage(shutdown_command)) {
                        shutting_down = true;
                    }
                }
                ...
                epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms);
            }

shutting_down 是定义在 init.cpp 中的 static 变量,没有赋值为flase的地方,即init的主线程的shutdown流程只能执行一次。


HandlePowerctlMessage 的执行流程:

HandlePowerctlMessage(shutdown_command) //参数就是向临时属性"sys.powerctl"中设的值
    DoReboot(cmd, command, reboot_target, run_fsck) //reboot_target是argv[2]如"bootloader"
        LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget; //有个打印
        property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip); //设置属性 persist.sys.boot.reason
        sync(); //将缓存刷回文件系统
        if (shutdown_timeout > 0ms) //默认不执行
            for (s : ServiceList::GetInstance()) //【1】先终止所有服务(毧失败下面还有杀掉)
                s->Terminate();
        for (s : ServiceList::GetInstance()) //【2】杀掉shutdown流程不需要的所有服务
            s->Stop();
        ShutdownVold(); //【3】send volume shutdown to vold
        voldService->Stop();
        sync();
        TryUmountAndFsck //【4】sync,尝试umount,并为shutdown运行fsck
            stat = UmountPartitions(timeout - t.duration());
            if (stat != UMOUNT_STAT_SUCCESS)
                KillAllProcesses
                    WriteStringToFile("i", "/proc/sysrq-trigger") //借助sysrq去杀所有进程
        sync();
        LogShutdownTime(stat, &t);
            LOG(WARNING) << "powerctl_shutdown_time_ms:" << t->duration().count() << ":" << stat; //有打印耗时
        RebootSystem(cmd, rebootTarget); //如往属性中写"shutdown",cmd=ANDROID_RB_POWEROFF, 若是"reboot"则 ANDROID_RB_RESTART2
            if (!IsRebootCapable()) //必须要有reboot内核的权限
                exit(0);
            switch (cmd)
                case ANDROID_RB_POWEROFF: //对应往属性中写"shutdown", 直接调用C库的reboot函数。
                    reboot(RB_POWER_OFF); //bionic/reboot.cpp
                        __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL); //mode=LINUX_REBOOT_CMD_POWER_OFF
                case ANDROID_RB_RESTART2: //对应往属性中写"reboot"
                    syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str()) //reboot系统调用,比如bootloader等。

上面用到的宏:
//bionic/libc/include/sys/reboot.h
#define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF

可以看到,无论是shutdown还是reboot,最终都会调用 reboot() 系统调用函数,其定义在内核 reboot.c 中。

 

三、内核代码流程

1. 调用流程

SYSCALL_DEFINE4(reboot, magic1, magic2, cmd, arg) //reboot.c
    ns_capable(pid_ns->user_ns, CAP_SYS_BOOT) //调用者必须要有root权限,且mgic要通过检查
    switch (cmd)
    case LINUX_REBOOT_CMD_HALT:
        kernel_halt();
        do_exit(0);
        panic("cannot halt");
    case LINUX_REBOOT_CMD_POWER_OFF: //"shutdown"走这里
        kernel_power_off();
            /* 通过 reboot_notifier_list 往外广播 SYS_POWER_OFF 并将全局 system_state 设置为
             * SYSTEM_POWER_OFF,然后调用各驱动的shutdown回调 */
            kernel_shutdown_prepare(SYSTEM_POWER_OFF);
            migrate_to_reboot_cpu() //将当前线程绑定到 reboot_cpu 上
            syscore_shutdown() //回调 syscore_ops_list 链表上所有 syscore_ops 的 ops->shutdown() 回调
            pr_emerg("Power down\n"); //检索没有这个打印
            kmsg_dump(KMSG_DUMP_POWEROFF); //将kernel log dump到dumper中, pstore注册了一个, pstore_register_kmsg()
            machine_power_off(); //向其它所有cpu发送 IPI_CPU_STOP 中断让其它cpu停下来
                pm_power_off
                    do_msm_poweroff //msm-poweroff.c
                        pr_notice("Powering off the SoC\n"); //没有打印出来
                        deassert_ps_hold(); //取消置位 PS_HOLD,向 PMIC 发出信号,告知我们已准备好关机或复位
                        pr_err("Powering off has failed\n"); //没有打印出来
        do_exit(0);
        break;
    case LINUX_REBOOT_CMD_RESTART2: //"reboot"走这里,arg是参数如"shell"/"bootloader
        kernel_restart(arg);
            kernel_restart_prepare(cmd);
            migrate_to_reboot_cpu();
            syscore_shutdown();
            if (!cmd) pr_emerg("Restarting system\n");
            else pr_emerg("Restarting system with command '%s'\n", cmd); //有打印,正常重启cmd="adb"/"shell"
            kmsg_dump(KMSG_DUMP_RESTART);
            machine_restart(cmd);
                pr_emerg("%s enter\n",__func__); //有打印出来
                smp_send_stop();
                arm_pm_restart
                    do_msm_restart //msm-poweroff.c
                        pr_notice("Going down for restart now\n"); //有打印出来
                        msm_restart_prepare //判断了reboot的类型,如"bootloader" "recovery" "rtc" 等####
                        deassert_ps_hold();
        break;

 

四、相关调试-日志

1. kernel log

//来自 last_kmsg.txt:
[   88.866737]  (0)[1:init]init: Received sys.powerctl='reboot,shell' from pid: 4207 (reboot) //表示在shell命令行中执行reboot命令导致重启的
[   88.866915]  (0)[1:init]init: Clear action queue and start shutdown trigger
[   88.867159]  (0)[1:init]init: processing action (shutdown_done) from (<Builtin Action>:0)
[   88.867197]  (0)[1:init]init: Reboot start, reason: reboot,shell, rebootTarget: shell
[   88.896010]  (3)[1:init]init: Shutdown timeout: 0 ms
[   88.896135]  (3)[1:init]init: Could not start shutdown critical service 'rmt_storage': Cannot find '/vendor/bin/rmt_storage': No such file or directory
[   88.896900]  (3)[1:init]init: starting service 'blank_screen'...
[   88.898827]  (3)[1:init]init: Sending signal 9 to service 'mdnsd' (pid 1977) process group...
[   88.904811]  (3)[1:init]libprocessgroup: Successfully killed process cgroup uid 1020 pid 1977 in 5ms
...
[   90.258250]  (2)[1:init]libprocessgroup: Failed to kill process cgroup uid 0 pid 551 in 257ms, 1 processes remain
[   90.279079]  (2)[1:init]init: Service 'incidentd' (pid 824) received signal 9
[   90.279371]  (2)[1:init]init: Service 'installd' (pid 825) received signal 9
...
[   90.788846]  (3)[1:init]init: Sending signal 9 to service 'logd' (pid 512) process group...
[   90.807763]  (1)[1:init]init: Unmounting /dev/block/bootdevice/by-name/vmap:/mnt/vmap opts rw,seclabel,nosuid,nodev,noatime,data=ordered
[   90.853054]  (0)[1:init]init: Umounted /dev/block/bootdevice/by-name/vmap:/mnt/vmap opts rw,seclabel,nosuid,nodev,noatime,data=ordered
...
[   91.676064]  (2)[1:init]init: powerctl_shutdown_time_ms:2808:0 //用户空间kill任务init进程执行时间
[   91.676121]  (2)[1:init]init: Reboot ending, jumping to kernel //进入内核
[   91.677918]  (2)[1:init]sd 0:0:0:0: [sda] Synchronizing SCSI cache
[   91.685350]  (2)[1:init]tda75610_mute_on_off_force: set mute 1
[   92.293744]  (0)[1:init]tda75610_shutdown: success
[   92.306601]  (2)[1:init][inap562t]inap562t_video_on,enter.--> 0 reset remote to aline
[   92.306630]  (2)[1:init][inap562t_remote]inap562t_clear_error clear error 
[   92.324104]  (2)[1:init][inap562t]panel_onOff_call,type 0x0 .
[   92.324133]  (2)[1:init][inap562t_remote]inap562t_remote_suspend 252
[   92.328938]  (3)[1:init][inap562t_remote]inap562t_setState 1562 screen state set  1  
[   93.345958]  (3)[1:init][inap562t_remote]suspend timeout 101 
[   93.345979]  (3)[1:init][inap562t_remote]inap562t_change_mode change mode to 4 
[   93.350692]  (3)[1:init]Unbalanced regulator off //这里出现了一个崩溃
[   93.362658]  (0)[601:composer@2.1-se]WARNING: at ffffff80085435c0 [verbose debug info unavailable]
[   93.425690]  (0)[1:init]reboot: Restarting system with command 'shell' //kernel_restart()中的打印, shell终端执行reboot命令默认参数就是shell
[   93.434779]  (0)[1:init]machine_restart enter //进入到 machine_restart()
[   93.462655]  (0)[1:init]Going down for restart now //do_msm_restart()中的打印

看起来init进程的日志主要在main log中。


2. main log

F:\>adb reboot HelloWorld_1
/data/Log/log0 # grep HelloWorld_1 -r ./
./last_kmsg.txt:[43795.524845]  (1)[1:init]init: Received sys.powerctl='reboot,HelloWorld_1' from pid: 688 (/system/bin/adbd)
./last_kmsg.txt:[43795.526667]  (1)[1:init]init: Reboot start, reason: reboot,HelloWorld_1, rebootTarget: HelloWorld_1
./last_kmsg.txt:[43800.509268]  (0)[1:init]reboot: Restarting system with command 'HelloWorld_1'
/data/Log/log1 #
/data/Log/log1 # grep HelloWorld_1 -r ./
/data/Log/log1 #
/data/Log # grep ShutdownThread -r ./
/data/Log #

adb重启,main log中默认是没有打印的。


若是上层逻辑触发的重启:

kernel.txt:
<14>[79443.634028]  (3)[1:init]init: Received sys.powerctl='reboot,soc:ota' from pid: 875 (system_server)

main.txt:
06-05 20:18:23.200   875  6689 I ShutdownThread: Shutting down package manager...
06-05 20:18:23.210   875  6691 I ShutdownThread: Waiting for Radio...
06-05 20:18:23.210   875  6691 I ShutdownThread: Radio shutdown complete.
06-05 20:18:23.211   875  6689 I ShutdownThread: Rebooting, reason: soc:ota

相关文件: /frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java


五、小结

1. 上层shutdown流程

向临时属性 sys.powerctl 中写 shutdown 或 reboot 触发关机或重启;
然后init主线程被唤醒,向所有 services 发送 terminate 信号, 并等待其退出;
如果 services 没有主动退出,则发送 kill 信号,强制杀掉 service;
给 vold 发送 shutdown 命令,主动卸载挂载的设备,例如SD卡;
执行 sync 操作,同步文件系统;
调用 kernel reboot 系统调用;


2. kernel shutdown流程

通过 notifier 机制广播将要 shutdown 或 reboot;
调用各设备的 shutdown 回调,优先级class > bus > driover;
调用各个 syscore_ops->shutdown() 回调;
将 kernel log dump 到 dumper 中, 其中pstore就注册了一个 dumper;
向其它所有cpu发送 IPI_CPU_STOP 中断让其它cpu停下来;
取消置位 PS_HOLD,向 PMIC 发出信号,告知我们已准备好关机或复位;

 

posted on 2025-06-17 22:10  Hello-World3  阅读(291)  评论(0)    收藏  举报

导航