Linux上层关机和reboot流程

从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 
#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进程处理关机的流程是:

  1. 先运行shutdown action list注销系统服务
  2. 运行kill(-1, SIGTERM);发送SIGTERM给系统中除了init外的所有进程
  3. 运行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系统的实现也大同小异,这里就不做赘述了

你可能感兴趣的:(Linux笔记)