void reboot_main(void)
{
int types[] = {RB_AUTOBOOT, RB_HALT_SYSTEM, RB_POWER_OFF},
sigs[] = {SIGTERM, SIGUSR1, SIGUSR2}, idx;
if (!(toys.optflags & FLAG_n)) sync();
idx = stridx("hp", *toys.which->name)+1;
if (toys.optflags & FLAG_f)
toys.exitval = reboot(types[idx]);
else
toys.exitval = kill(1, sigs[idx]);
}
通过查看toybox中reboot命令的实现,如果存在-f选项,那么会不发送信号到init,而是直接reboot,如果不存在-f选项,那么会发送信号给init,后续关机流程由init接管。之所以要交给init处理,是为了让init对上层app进行通知处理,从而在关机前能够保存用户数据。
toys.exitval = kill(1, sigs[idx]);
这一条指令就是发送signal到进程号为1的进程,也就是我们常说的init进程。可以看到不同的reboot type,对应着不同的signale type:
RB_AUTOBOOT --- SIGTERM
RB_HALT_SYSTEM --- SIGUSR1
RB_POWER_OFF --- SIGUSR2
我们先跟一下reboot这一条线:
#include
#include
extern "C" int __reboot(int, int, int, void*);
int reboot(int mode) {
return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
}
对于arm64平台它的进一步底层实现为一个系统调用bionic/libc/arch-arm64/syscalls/__reboot.S:
#include
ENTRY(__reboot)
mov x8, __NR_reboot
svc #0
cmn x0, #(MAX_ERRNO + 1)
cneg x0, x0, hi
b.hi __set_errno_internal
ret
END(__reboot)
.hidden __reboot
svc会触发系统调用进入内核执行对应的系统调用。对应于magic值的定义都是统一的:
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793
对于另外一条线,发送信号给init进程,那么后续的动作全部都会交由init进程去处理了,init进程处理和上面的方式不同的地方就是会进行一些关机前的操作,并且最终同样使用reboot系统调用触发重启或者关机。
static void set_default(void)
{
sigset_t signal_set_c;
sigatexit(SIG_DFL);
sigfillset(&signal_set_c);
sigprocmask(SIG_UNBLOCK,&signal_set_c, NULL);
run_action_from_list(SHUTDOWN);
error_msg("The system is going down NOW!");
kill(-1, SIGTERM);
error_msg("Sent SIGTERM to all processes");
sync();
sleep(1);
kill(-1,SIGKILL);
sync();
}
static void halt_poweroff_reboot_handler(int sig_no)
{
unsigned int reboot_magic_no = 0;
pid_t pid;
set_default();
switch (sig_no) {
case SIGUSR1:
error_msg("Requesting system halt");
reboot_magic_no=RB_HALT_SYSTEM;
break;
case SIGUSR2:
error_msg("Requesting system poweroff");
reboot_magic_no=RB_POWER_OFF;
break;
case SIGTERM:
error_msg("Requesting system reboot");
reboot_magic_no=RB_AUTOBOOT;
break;
default:
break;
}
sleep(1);
pid = vfork();
if (pid == 0) {
reboot(reboot_magic_no);
_exit(EXIT_SUCCESS);
}
while(1) sleep(1);
}
init进程处理关机的流程是:
做过高通项目的同学会发现高通自己实现了reboot的命令程序,而没有使用toybox。
#define ANDROID_RB_PROPERTY "sys.powerctl"
ret = property_set(ANDROID_RB_PROPERTY, property_val);
if (ret < 0) {
perror(cmd);
exit(EXIT_FAILURE);
}
reboot命令最终是通过设置一个android property来做此操作的,该property是sys.powerctl。那么设置此property之后系统会执行些什么呢?实际上在init进程中会监控此property并触发对应的操作。它的函数调用栈为:
property_changed -> on property sys.powerctl -> HandlePowerctlMessage ${sys.powerctl}
其init实现的内容和上面介绍的Linux系统的实现也大同小异,这里就不做赘述了