linux reboot 实现流程

2019-07-12 23:52发布

原文地址:http://blog.csdn.net/davion_zhang/article/details/52233043

一、版本说明

嵌入式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的情况。 代码如下 [cpp] view plain copy print?
  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将部分信号进行重定义
[cpp] view plain copy print?
  1. bb_signals_recursive_norestart(0  
  2.     + (1 << SIGINT)  /* Ctrl-Alt-Del */  
  3.     + (1 << SIGQUIT) /* re-exec another init */  
  4. fdef SIGPWR  
  5.     + (1 << SIGPWR)  /* halt */  
  6. ndif  
  7.     + (1 << SIGUSR1) /* halt */  
  8.     + (1 << SIGTERM) /* reboot */  
  9.     + (1 << SIGUSR2) /* poweroff */  
  10. f ENABLE_FEATURE_USE_INITTAB  
  11.     + (1 << SIGHUP)  /* reread /etc/inittab */  
  12. ndif  
  13.     , record_signo);  

[cpp] view plain copy print?
  1. void record_signo(int signo)  
  2. {  
  3.     bb_got_signal = signo;  
  4. }  
将SIGUSR1(halt) SIGUSR2(poweroff) SIGTERM(reboot)信号存入全局变量bb_got_signal中。 
在init_main的最后进入一个while(1)循环,不断检查信号和等待子进程的退出 其中check_delayed_sigs就是用来检查这个全局变量的,如下: [cpp] view plain copy print?
  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成功,然后跳出循环检查信号。 下面看一下信号的处理部分 [cpp] view plain copy print?
  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函数
[cpp] view plain copy print?
  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函数将信号重置回默认处理。
[cpp] view plain copy print?
  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信号是不可被屏蔽的,
这样告诉其他进程必须终止。 [cpp] view plain copy print?
  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函数 [cpp] view plain copy print?
  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 [cpp] view plain copy print?
  1. int  
  2. reboot (int howto)  
  3. {  
  4.   return INLINE_SYSCALL (reboot, 3, (int) 0xfee1dead, 672274793, howto);  
  5. }  

4.内核系统调用 kernel/sys.c
[cpp] view plain copy print?
  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 [cpp] view plain copy print?
  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 ");  
  10.     local_irq_disable();  
  11.     while (1) ;  
  12. }  
这个函数之后就与具体的架构有关系了。 下面是powerpc P1020芯片的复位 ppc_md.restart(cmd);的函数原型在/arch/powerpc/platforms/85xx中定义
[cpp] view plain copy print?
  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. };  

[cpp] view plain copy print?
  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的复位