Linux reboot全过程


一、版本说明

嵌入式Linux 下面的reboot命令看似简单,但出问题时定位起来发现别有洞天。

下面就按在shell下执行reboot命令之后程序的执行过程进行解析。

Busybox:1.23.2                        ——制作跟文件系统,/sbin/reboot程序的由来

Libc:2.6.1                                  ——标准C库

Linux kernel:2.6.35                 ——内核版本

 

二、流程简介

         如图所示是reboot的简要流程图。

 


 普通的reboot是通过busybox为入口,进入halt_main函数,然后给init进程发送SIGTERM信号,init进程接收到信号后给其他进程发送终止信号,最后调用C库函数reboot,reboot通过系统调用sys_reboot进入内核,内核将整个系统重启。其中在shell中执行reboot –f则通过halt_main直接调用C函数reboot,不经过init进程。

三、代码详解

1.reboot命令端

执行reboot命令,busybox检查当前命令为reboot,进入函数halt_main,

reboot,halt和poweroff都会进入这个函数,不同的命令发送的信号和执行的操作不同。

现只分析reboot的情况。

代码如下

 

  1.  
    int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  2.  
    int halt_main(int argc UNUSED_PARAM, char **argv)
  3.  
    {
  4.  
    static const int magic[] = {
  5.  
    RB_HALT_SYSTEM,
  6.  
    RB_POWER_OFF,
  7.  
    RB_AUTOBOOT
  8.  
    };
  9.  
    static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
  10.  
     
  11.  
    int delay = 0;
  12.  
    int which, flags, rc;
  13.  
     
  14.  
    /* Figure out which applet we're running */
  15.  
    for (which = 0; "hpr"[which] != applet_name[0]; which++)
  16.  
    continue;
  17.  
     
  18.  
    /* Parse and handle arguments */
  19.  
    opt_complementary = "d+"; /* -d N */
  20.  
    /* We support -w even if !ENABLE_FEATURE_WTMP,
  21.  
    * in order to not break scripts.
  22.  
    * -i (shut down network interfaces) is ignored.
  23.  
    */
  24.  
    flags = getopt32(argv, "d:nfwi", &delay);
  25.  
     
  26.  
    sleep(delay);
  27.  
     
  28.  
    write_wtmp();
  29.  
     
  30.  
    if (flags & 8) /* -w */
  31.  
    return EXIT_SUCCESS;
  32.  
     
  33.  
    if (!(flags & 2)) /* no -n */
  34.  
    sync();
  35.  
     
  36.  
    /* Perform action. */
  37.  
    rc = 1;
  38.  
    if (!(flags & 4)) { /* no -f */
  39.  
    //TODO: I tend to think that signalling linuxrc is wrong
  40.  
    // pity original author didn't comment on it...
  41.  
    if (ENABLE_FEATURE_INITRD) {
  42.  
    /* talk to linuxrc */
  43.  
    /* bbox init/linuxrc assumed */
  44.  
    pid_t *pidlist = find_pid_by_name("linuxrc");
  45.  
    if (pidlist[0] > 0)
  46.  
    rc = kill(pidlist[ 0], signals[which]);
  47.  
    if (ENABLE_FEATURE_CLEAN_UP)
  48.  
    free(pidlist);
  49.  
    }
  50.  
    if (rc) {
  51.  
    /* talk to init */
  52.  
    if (!ENABLE_FEATURE_CALL_TELINIT) {
  53.  
    /* bbox init assumed */
  54.  
    rc = kill( 1, signals[which]);
  55.  
    } else {
  56.  
    /* SysV style init assumed */
  57.  
    /* runlevels:
  58.  
    * 0 == shutdown
  59.  
    * 6 == reboot */
  60.  
    execlp(CONFIG_TELINIT_PATH,
  61.  
    CONFIG_TELINIT_PATH,
  62.  
    which == 2 ? "6" : "0",
  63.  
    ( char *)NULL
  64.  
    );
  65.  
    bb_perror_msg_and_die( "can't execute '%s'",
  66.  
    CONFIG_TELINIT_PATH);
  67.  
    }
  68.  
    }
  69.  
    } else {
  70.  
    rc = reboot(magic[which]);
  71.  
    }
  72.  
     
  73.  
    if (rc)
  74.  
    bb_perror_nomsg_and_die();
  75.  
    return rc;
  76.  
    }

 

该函数判断reboot是否带了 -f 参数,如果带了,直接调用reboot调用C函数库

如果没带,则通过

kill(1, signals[which]);

给init进程发送SIGTERM信号。

 

2.init进程端

 

init进程初始化函数init_main将部分信号进行重定义

  1.  
    bb_signals_recursive_norestart( 0
  2.  
    + ( 1 << SIGINT) /* Ctrl-Alt-Del */
  3.  
    + ( 1 << SIGQUIT) /* re-exec another init */
  4.  
    #ifdef SIGPWR
  5.  
    + ( 1 << SIGPWR) /* halt */
  6.  
    #endif
  7.  
    + ( 1 << SIGUSR1) /* halt */
  8.  
    + ( 1 << SIGTERM) /* reboot */
  9.  
    + ( 1 << SIGUSR2) /* poweroff */
  10.  
    # if ENABLE_FEATURE_USE_INITTAB
  11.  
    + ( 1 << SIGHUP) /* reread /etc/inittab */
  12.  
    #endif
  13.  
    , record_signo);

 

 

 

  1.  
    void record_signo(int signo)
  2.  
    {
  3.  
    bb_got_signal = signo;
  4.  
    }
  5.  
     

将SIGUSR1(halt) SIGUSR2(poweroff) SIGTERM(reboot)信号存入全局变量bb_got_signal中。 
在init_main的最后进入一个while(1)循环,不断检查信号和等待子进程的退出

其中check_delayed_sigs就是用来检查这个全局变量的,如下:

 

  1.  
    while (1) {
  2.  
    int maybe_WNOHANG;
  3.  
     
  4.  
    maybe_WNOHANG = check_delayed_sigs();
  5.  
     
  6.  
    /* (Re)run the respawn/askfirst stuff */
  7.  
    run_actions(RESPAWN | ASKFIRST);
  8.  
    maybe_WNOHANG |= check_delayed_sigs();
  9.  
     
  10.  
    /* Don't consume all CPU time - sleep a bit */
  11.  
    sleep( 1);
  12.  
    maybe_WNOHANG |= check_delayed_sigs();
  13.  
     
  14.  
    /* Wait for any child process(es) to exit.
  15.  
    *
  16.  
    * If check_delayed_sigs above reported that a signal
  17.  
    * was caught, wait will be nonblocking. This ensures
  18.  
    * that if SIGHUP has reloaded inittab, respawn and askfirst
  19.  
    * actions will not be delayed until next child death.
  20.  
    */
  21.  
    if (maybe_WNOHANG)
  22.  
    maybe_WNOHANG = WNOHANG;
  23.  
    while (1) {
  24.  
    pid_t wpid;
  25.  
    struct init_action *a;
  26.  
     
  27.  
    /* If signals happen _in_ the wait, they interrupt it,
  28.  
    * bb_signals_recursive_norestart set them up that way
  29.  
    */
  30.  
    wpid = waitpid( -1, NULL, maybe_WNOHANG);
  31.  
    if (wpid <= 0)
  32.  
    break;
  33.  
     
  34.  
    a = mark_terminated(wpid);
  35.  
    if (a) {
  36.  
    message(L_LOG, "process '%s' (pid %d) exited. "
  37.  
    "Scheduling for restart.",
  38.  
    a->command, wpid);
  39.  
    }
  40.  
    /* See if anyone else is waiting to be reaped */
  41.  
    maybe_WNOHANG = WNOHANG;
  42.  
    }
  43.  
    } /* while (1) */

而里面的while(1)一般会阻塞在waitpid中,那么信号检查是不是会有问题?

  • WNOHANG        如果没有可用的子进程退出状态,立即返回而不是阻塞

但maybe_WNOHANG的值应该是0,不是WNOHANG(=1)感觉还是会阻塞。我这样理解的,因为所有的用户进程都是init进程的子进程,我判断前面执行reboot时也是一个子进程,halt_main发送完信号后就会退出,init接收到信号而且waitpid成功,然后跳出循环检查信号。

 

下面看一下信号的处理部分

 

  1.  
    static int check_delayed_sigs(void)
  2.  
    {
  3.  
    int sigs_seen = 0;
  4.  
     
  5.  
    while (1) {
  6.  
    smallint sig = bb_got_signal;
  7.  
     
  8.  
    if (!sig)
  9.  
    return sigs_seen;
  10.  
    bb_got_signal = 0;
  11.  
    sigs_seen = 1;
  12.  
    #if ENABLE_FEATURE_USE_INITTAB
  13.  
    if (sig == SIGHUP)
  14.  
    reload_inittab();
  15.  
    #endif
  16.  
    if (sig == SIGINT)
  17.  
    run_actions(CTRLALTDEL);
  18.  
    if (sig == SIGQUIT) {
  19.  
    exec_restart_action();
  20.  
    /* returns only if no restart action defined */
  21.  
    }
  22.  
    if ((1 << sig) & (0
  23.  
    #ifdef SIGPWR
  24.  
    + ( 1 << SIGPWR)
  25.  
    #endif
  26.  
    + ( 1 << SIGUSR1)
  27.  
    + ( 1 << SIGUSR2)
  28.  
    + ( 1 << SIGTERM)
  29.  
    )) {
  30.  
    halt_reboot_pwoff(sig);
  31.  
    }
  32.  
    }
  33.  
    }

判断为SIGTERM进入halt_reboot_pwoff函数

 

  1.  
    static void halt_reboot_pwoff(int sig)
  2.  
    {
  3.  
    const char *m;
  4.  
    unsigned rb;
  5.  
     
  6.  
    /* We may call run() and it unmasks signals,
  7.  
    * including the one masked inside this signal handler.
  8.  
    * Testcase which would start multiple reboot scripts:
  9.  
    * while true; do reboot; done
  10.  
    * Preventing it:
  11.  
    */
  12.  
    reset_sighandlers_and_unblock_sigs();
  13.  
     
  14.  
    run_shutdown_and_kill_processes();
  15.  
     
  16.  
    m = "halt";
  17.  
    rb = RB_HALT_SYSTEM;
  18.  
    if (sig == SIGTERM) {
  19.  
    m = "reboot";
  20.  
    rb = RB_AUTOBOOT;
  21.  
    } else if (sig == SIGUSR2) {
  22.  
    m = "poweroff";
  23.  
    rb = RB_POWER_OFF;
  24.  
    }
  25.  
    message(L_CONSOLE, "Requesting system %s", m);
  26.  
    pause_and_low_level_reboot(rb);
  27.  
    /* not reached */
  28.  
    }

 

 

reset_sighandlers_and_unblock_sigs函数将信号重置回默认处理。

 

  1.  
    static void reset_sighandlers_and_unblock_sigs(void)
  2.  
    {
  3.  
    bb_signals( 0
  4.  
    + ( 1 << SIGUSR1)
  5.  
    + ( 1 << SIGUSR2)
  6.  
    + ( 1 << SIGTERM)
  7.  
    + ( 1 << SIGQUIT)
  8.  
    + ( 1 << SIGINT)
  9.  
    + ( 1 << SIGHUP)
  10.  
    + ( 1 << SIGTSTP)
  11.  
    + ( 1 << SIGSTOP)
  12.  
    , SIG_DFL);
  13.  
    sigprocmask_allsigs(SIG_UNBLOCK);
  14.  
    }

run_shutdown_and_kill_processes函数给所有进程发送SIGTERM信号并执行sync(保存数据)

 

延时后再次发送SIGKILL信号,这里说明一下为什么要发送SIGKILL信号,一般的SIGINT和SIGTERM信号都可以屏蔽或转作他用,SIGKILL信号是不可被屏蔽的,

这样告诉其他进程必须终止。

  1.  
    static void run_shutdown_and_kill_processes(void)
  2.  
    {
  3.  
    /* Run everything to be run at "shutdown". This is done _prior_
  4.  
    * to killing everything, in case people wish to use scripts to
  5.  
    * shut things down gracefully... */
  6.  
    run_actions(SHUTDOWN);
  7.  
     
  8.  
    message(L_CONSOLE | L_LOG, "The system is going down NOW!");
  9.  
     
  10.  
    /* Send signals to every process _except_ pid 1 */
  11.  
    kill( -1, SIGTERM);
  12.  
    message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
  13.  
    sync();
  14.  
    sleep( 1);
  15.  
     
  16.  
    kill( -1, SIGKILL);
  17.  
    message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
  18.  
    sync();
  19.  
    /*sleep(1); - callers take care about making a pause */
  20.  
    }

最终进入函数pause_and_low_level_reboot,起一个轻量级进程执行reboot标准C函数

 

 

  1.  
    static void pause_and_low_level_reboot(unsigned magic)
  2.  
    {
  3.  
    pid_t pid;
  4.  
     
  5.  
    /* Allow time for last message to reach serial console, etc */
  6.  
    sleep( 1);
  7.  
     
  8.  
    /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
  9.  
    * in linux/kernel/sys.c, which can cause the machine to panic when
  10.  
    * the init process exits... */
  11.  
    pid = vfork();
  12.  
    if (pid == 0) { /* child */
  13.  
    reboot(magic);
  14.  
    _exit(EXIT_SUCCESS);
  15.  
    }
  16.  
    while (1)
  17.  
    sleep( 1);
  18.  
    }

到这里busybox里面的内容全部处理完。

 

 

 

3.标准C函数reboot

前面执行reboot -f 就是直接执行的这个函数

 

reboot函数比较简单,直接进行系统调用进入内核。(0xffe1dead  feeldead这个魔术还是比较有意思的)

其中参数howto为RB_AUTOBOOT=0x01234567

sysdeps/unix/sysv/linux/reboot.c

 

  1.  
    int
  2.  
    reboot (int howto)
  3.  
    {
  4.  
    return INLINE_SYSCALL (reboot, 3, (int) 0xfee1dead, 672274793, howto);
  5.  
    }


4.内核系统调用

 

kernel/sys.c

  1.  
    SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
  2.  
    void __user *, arg)
  3.  
    {
  4.  
    。。。
  5.  
     
  6.  
    mutex_lock(&reboot_mutex);
  7.  
    switch (cmd) {
  8.  
    case LINUX_REBOOT_CMD_RESTART:
  9.  
    kernel_restart( NULL);
  10.  
    break;
  11.  
     
  12.  
    case LINUX_REBOOT_CMD_CAD_ON:
  13.  
    C_A_D = 1;
  14.  
    break;
  15.  
     
  16.  
    case LINUX_REBOOT_CMD_CAD_OFF:
  17.  
    C_A_D = 0;
  18.  
    break;
  19.  
     
  20.  
    case LINUX_REBOOT_CMD_HALT:
  21.  
    kernel_halt();
  22.  
    do_exit( 0);
  23.  
    panic( "cannot halt");
  24.  
     
  25.  
    case LINUX_REBOOT_CMD_POWER_OFF:
  26.  
    kernel_power_off();
  27.  
    do_exit( 0);
  28.  
    break;
  29.  
     
  30.  
    。。。
  31.  
     
  32.  
    default:
  33.  
    ret = -EINVAL;
  34.  
    break;
  35.  
    }
  36.  
    mutex_unlock(&reboot_mutex);
  37.  
    return ret;
  38.  
    }

进入

 

case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
调用kernel_restart函数

——>machine_restart

 

  1.  
    void machine_restart(char *cmd)
  2.  
    {
  3.  
    machine_shutdown();
  4.  
    if (ppc_md.restart)
  5.  
    ppc_md.restart(cmd);
  6.  
    #ifdef CONFIG_SMP
  7.  
    smp_send_stop();
  8.  
    #endif
  9.  
    printk(KERN_EMERG "System Halted, OK to turn off power\n");
  10.  
    local_irq_disable();
  11.  
    while (1) ;
  12.  
    }

这个函数之后就与具体的架构有关系了。

 

下面是powerpc P1020芯片的复位

ppc_md.restart(cmd);的函数原型在/arch/powerpc/platforms/85xx中定义

 

  1.  
    define_machine(p2020_rdb_pc) {
  2.  
    .name = "P2020RDB-PC",
  3.  
    .probe = p2020_rdb_pc_probe,
  4.  
    .setup_arch = mpc85xx_rdb_setup_arch,
  5.  
    .init_IRQ = mpc85xx_rdb_pic_init,
  6.  
    #ifdef CONFIG_PCI
  7.  
    .pcibios_fixup_bus = fsl_pcibios_fixup_bus,
  8.  
    #endif
  9.  
    .get_irq = mpic_get_irq,
  10.  
    .restart = fsl_rstcr_restart,
  11.  
    .calibrate_decr = generic_calibrate_decr,
  12.  
    .progress = udbg_progress,
  13.  
    };

 

  1.  
    void fsl_rstcr_restart(char *cmd)
  2.  
    {
  3.  
    local_irq_disable();
  4.  
    if (rstcr)
  5.  
    /* set reset control register */
  6.  
    out_be32(rstcr, 0x2); /* HRESET_REQ */
  7.  
     
  8.  
    while (1) ;
  9.  
    }

最终cpu往寄存器Reset control register(0x000E_00B0)中写2

 

也就是往管脚HRESET_REQ发出了一个信号,该信号应该与HRESET硬复位管脚相连

这样就实现了CPU的复位


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM