M 平台reboot 流程分析

reboot 在我们开发过程中,已经软件固件升级/recovery过程中经常使用到。

重启工具

# which  reboot                                                    
/system/bin/reboot

在Android O里面reboot工具的源码在

system\core\reboot\reboot.c

reboot 工具分析

在system\core\reboot\reboot.c,里面

int main(int argc, char *argv[])
{
    int ret;
    size_t prop_len;
    char property_val[PROPERTY_VALUE_MAX];
    const char *cmd = "reboot";
    char *optarg = "";

    opterr = 0;
    do {
        int c;

        c = getopt(argc, argv, "p");

        if (c == -1) {
            break;
        }

        switch (c) {
        case 'p':
            cmd = "shutdown";
            break;
        case '?':
            fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    } while (1);

    if(argc > optind + 1) {
        fprintf(stderr, "%s: too many arguments\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (argc > optind)
        optarg = argv[optind];

    prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
    if (prop_len >= sizeof(property_val)) {
        fprintf(stderr, "reboot command too long: %s\n", optarg);
        exit(EXIT_FAILURE);
    }

    ret = property_set(ANDROID_RB_PROPERTY, property_val);
    if(ret < 0) {
        perror("reboot");
        exit(EXIT_FAILURE);
    }

    // Don't return early. Give the reboot command time to take effect
    // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
    while(1) { pause(); }

    fprintf(stderr, "Done\n");
    return 0;
}

在android_reboot.h中
/* Properties */
#define ANDROID_RB_PROPERTY "sys.powerctl"

/* Android reboot reason stored in this file */
#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"

解析命令reboot 参数,之后,设置sys.powerctl, 也即

# setprop sys.powerctrl reboot 

设置完环境变量之后,下一步就要解析环境变量了。

环境变量解析

设置环境会进入,system\core\init\property_service.cpp文件中。

uint32_t property_set(const std::string& name, const std::string& value) {
    size_t valuelen = value.size();

    if (!is_legal_property_name(name)) {
        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
        return PROP_ERROR_INVALID_NAME;
    }

    ......
    property_changed(name, value);
    return PROP_SUCCESS;
}

设置完之后,property_changed会针对特殊变量做一些处理。

void property_changed(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    if (name == "sys.powerctl") HandlePowerctlMessage(value);

	......
}

HandlePowerctlMessage开始接管"sys.powerctl" 的变化

bool HandlePowerctlMessage(const std::string& command) {
    unsigned int cmd = 0;
    std::vector<std::string> cmd_params = android::base::Split(command, ",");
    std::string reboot_target = "";
    bool run_fsck = false;
    bool command_invalid = false;

    if (cmd_params.size() > 3) {
        command_invalid = true;
    } else if (cmd_params[0] == "shutdown") {
        cmd = ANDROID_RB_POWEROFF;
        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
            // Run fsck once the file system is remounted in read-only mode.
            run_fsck = true;
        }
    } else if (cmd_params[0] == "reboot") {
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            reboot_target = cmd_params[1];
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            if (reboot_target == "bootloader") {
                std::string err;
                if (!write_reboot_bootloader(&err)) {
                    LOG(ERROR) << "reboot-bootloader: Error writing "
                                  "bootloader_message: "
                               << err;
                }
            }
            // If there is an additional bootloader parameter, pass it along
            if (cmd_params.size() == 3) {
                reboot_target += "," + cmd_params[2];
            }
        }
    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
        cmd = ANDROID_RB_THERMOFF;
    } else {
        command_invalid = true;
    }
    if (command_invalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return false;
    }

    DoReboot(cmd, command, reboot_target, run_fsck);
    return true;
}

解析reboot 参数,cmd 为ANDROID_RB_RESTART2,接着调用DoReboot

void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
              bool runFsck) {
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;

    android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
                                     S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);

    if (cmd == ANDROID_RB_THERMOFF) {  // do not wait if it is thermal
        DoThermalOff();
        abort();
    }

    constexpr unsigned int shutdownTimeoutDefault = 6;
    unsigned int shutdownTimeout = shutdownTimeoutDefault;
    if (SHUTDOWN_ZERO_TIMEOUT) {  // eng build
        shutdownTimeout = 0;
    } else {
        shutdownTimeout =
            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
    }
    LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;

    // keep debugging tools until non critical ones are all gone.
    const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
    const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
    ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
        if (kill_after_apps.count(s->name())) {
            s->SetShutdownCritical();
        } else if (to_starts.count(s->name())) {
            s->Start();
            s->SetShutdownCritical();
        }
    });

    Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
    Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
    if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
        ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
            s->SetShutdownCritical();  // will not check animation class separately
        });
    }

    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (shutdownTimeout > 0) {
        LOG(INFO) << "terminating init services";

        // Ask all services to terminate except shutdown critical ones.
        ServiceManager::GetInstance().ForEachService([](Service* s) {
            if (!s->IsShutdownCritical()) s->Terminate();
        });

        int service_count = 0;
        // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
        unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
        while (t.duration_s() < terminationWaitTimeout) {
            ServiceManager::GetInstance().ReapAnyOutstandingChildren();

            service_count = 0;
            ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
                // Count the number of services running except shutdown critical.
                // Exclude the console as it will ignore the SIGTERM signal
                // and not exit.
                // Note: SVC_CONSOLE actually means "requires console" but
                // it is only used by the shell.
                if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
                    service_count++;
                }
            });

            if (service_count == 0) {
                // All terminable services terminated. We can exit early.
                break;
            }

            // Wait a bit before recounting the number or running services.
            std::this_thread::sleep_for(50ms);
        }
        LOG(INFO) << "Terminating running services took " << t
                  << " with remaining services:" << service_count;
    }

    // minimum safety steps before restarting
    // 2. kill all services except ones that are necessary for the shutdown sequence.
    ServiceManager::GetInstance().ForEachService([](Service* s) {
        if (!s->IsShutdownCritical()) s->Stop();
    });
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();

    // 3. send volume shutdown to vold
    Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
    if (voldService != nullptr && voldService->IsRunning()) {
        ShutdownVold();
        voldService->Stop();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }
    // logcat stopped here
    ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
        if (kill_after_apps.count(s->name())) s->Stop();
    });
    // 4. sync, try umount, and optionally run fsck for user shutdown
    sync();
    UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
    // Follow what linux shutdown is doing: one more sync with little bit delay
    sync();
    std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);
    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, rebootTarget);
    abort();
}

这个里面工作比较多,先关闭“tombstoned”,“logd”,“adbd”,“bootanim”,“surfaceflinger”
接着遍历所有的service,关闭除“关闭服务”之外的所有服务,

        // Ask all services to terminate except shutdown critical ones.
        ServiceManager::GetInstance().ForEachService([](Service* s) {
            if (!s->IsShutdownCritical()) s->Terminate();
        });

然后停止所有服务

    // 2. kill all services except ones that are necessary for the shutdown sequence.
    ServiceManager::GetInstance().ForEachService([](Service* s) {
        if (!s->IsShutdownCritical()) s->Stop();
    });

接着停止U盘服务

    // 3. send volume shutdown to vold
    Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
    if (voldService != nullptr && voldService->IsRunning()) {
        ShutdownVold();
        voldService->Stop();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }

再停止log,完了sync,最后进入RebootSystem,完成内核调用

内核调用

static void __attribute__((noreturn))
RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    LOG(INFO) << "Reboot ending, jumping to kernel";
    switch (cmd) {
        case ANDROID_RB_POWEROFF:
            reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
            break;

        case ANDROID_RB_THERMOFF:
            reboot(RB_POWER_OFF);
            break;
    }
    // In normal case, reboot should not return.
    PLOG(FATAL) << "reboot call returned";
    abort();
}

cmd 为ANDROID_RB_RESTART2 ,进入

syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());

接着进入内核,完成机器的的清理操作。

拓展

我们这里在分析一下,前面

        // Ask all services to terminate except shutdown critical ones.
        ServiceManager::GetInstance().ForEachService([](Service* s) {
            if (!s->IsShutdownCritical()) s->Terminate();
        });
void Service::Terminate() {
    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
    flags_ |= SVC_DISABLED;
    if (pid_) {
        KillProcessGroup(SIGTERM);
        NotifyStateChange("stopping");
    }
}

停止信号,发送一个SIGTERM 消息,然后通知了一下服务的状态变化。

  1. 发送SIGTERM消息
void Service::KillProcessGroup(int signal) {
    LOG(INFO) << "Sending signal " << signal
              << " to service '" << name_
              << "' (pid " << pid_ << ") process group...";
    int r;
    if (signal == SIGTERM) {
        r = killProcessGroupOnce(uid_, pid_, signal);
    } else {
        r = killProcessGroup(uid_, pid_, signal);
    }
    if (r == -1) {
        PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
    }
    if (kill(-pid_, signal) == -1) {
        PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
    }
}
static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();

    int processes;
    while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
        LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
        if (retry > 0) {
            std::this_thread::sleep_for(5ms);
            --retry;
        } else {
            LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
                       << initialPid;
            break;
        }
    }

    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();

    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
                 << static_cast<int>(ms) << "ms, " << processes << " procs remain";

    if (processes == 0) {
        return removeProcessGroup(uid, initialPid);
    } else {
        return -1;
    }
}
static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
    int processes = 0;
    struct ctx ctx;
    pid_t pid;

    ctx.initialized = false;

    while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
        processes++;
        if (pid == 0) {
            // Should never happen...  but if it does, trying to kill this
            // will boomerang right back and kill us!  Let's not let that happen.
            LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
            continue;
        }
        LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
                     << " as part of process group " << initialPid;
        if (kill(pid, signal) == -1) {
            PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
        }
    }

    if (ctx.initialized) {
        close(ctx.fd);
    }

    return processes;
}

最后会调用kill() 来杀掉服务。 也即命令行执行 kill -TERM ${PID}。对应服务可以通过中断来捕获SIGTERM消息。
更多kill 中断信号可以通过“kill命令”了解,以及killall

  1. 状态变化通知
void Service::NotifyStateChange(const std::string& new_state) const {
    if ((flags_ & SVC_TEMPORARY) != 0) {
        // Services created by 'exec' are temporary and don't have properties tracking their state.
        return;
    }

    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
    property_set(prop_name.c_str(), new_state.c_str());

    if (new_state == "running") {
        uint64_t start_ns = time_started_.time_since_epoch().count();
        property_set(StringPrintf("ro.boottime.%s", name_.c_str()).c_str(),
                     StringPrintf("%" PRIu64, start_ns).c_str());
    }
}

在running 的系统中,我们可以发现以下服务

 # getprop |grep init.svc                         
[init.svc.adbd]: [running]
[init.svc.audio-hal-2-0]: [running]
[init.svc.audioserver]: [running]
[init.svc.bluetooth-1-0]: [running]
[init.svc.bootanim]: [stopped]
[init.svc.camera-provider-2-4]: [running]
[init.svc.cameraserver]: [running]
[init.svc.configstore-hal-1-0]: [running]
[init.svc.console]: [running]
[init.svc.drm]: [running]
[init.svc.drm-hal-1-0]: [running]
[init.svc.gatekeeperd]: [running]
[init.svc.gralloc-2-0]: [running]
[init.svc.healthd]: [running]
[init.svc.hidl_memory]: [running]
[init.svc.hostapd]: [stopped]
[init.svc.hwservicemanager]: [running]
[init.svc.installd]: [running]
[init.svc.intertaca]: [stopped]
[init.svc.keymaster-3-0]: [running]
[init.svc.keystore]: [running]
[init.svc.lmkd]: [running]
[init.svc.loadwifi]: [stopped]
[init.svc.logd]: [running]
[init.svc.logd-reinit]: [stopped]
[init.svc.mdnsd]: [running]
[init.svc.media]: [running]
[init.svc.mediacodec]: [running]
[init.svc.mediadrm]: [running]
[init.svc.mediaextractor]: [running]
[init.svc.mediametrics]: [running]
[init.svc.memtrack-hal-1-0]: [running]
[init.svc.netd]: [running]
[init.svc.optee]: [running]
[init.svc.rel_supplicant]: [running]
[init.svc.resourcemanager]: [running]
[init.svc.ril-daemon]: [running]
[init.svc.servicemanager]: [running]
[init.svc.storaged]: [running]
[init.svc.surfaceflinger]: [running]
[init.svc.tombstoned]: [running]
[init.svc.tvservice]: [running]
[init.svc.ueventd]: [running]
[init.svc.virtualkeypad]: [running]
[init.svc.vndservicemanager]: [running]
[init.svc.vold]: [running]
[init.svc.webview_zygote32]: [running]
[init.svc.wificond]: [running]
[init.svc.wpa_supplicant]: [stopped]
[init.svc.zygote]: [running]
[init.svc.zygote_secondary]: [running]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值