從reboot指令開始
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 <unistd.h>
#include <sys/reboot.h>
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 <private/bionic_asm.h>
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程序處理關機的流程是:
- 先運作shutdown action list登出系統服務
- 運作kill(-1, SIGTERM);發送SIGTERM給系統中除了init外的所有程序
- 運作kill(-1,SIGKILL);發送SIGKILL給系統中除了init外的所有程序
Android平台的實作
做過高通項目的同學會發現高通自己實作了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系統的實作也大同小異,這裡就不做贅述了