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 消息,然后通知了一下服务的状态变化。
- 发送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
- 状态变化通知
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]