基于Linux与Busybox的Reboot命令流程分析

轉載: http://blog.csdn.net/wavemcu/article/details/8544333

 

一:Busyobx层的分析

        这段时间,在忙到一个项目时,需要在busybox中用到reboot命令,开始在busybox中的shell中输入reboot命令,始终如下的信 息,然后就停止在那里了,无法重启...为了彻底的弄明白这个问题,我在网络上找了很久,终于有个人写的一个reboot流程分析,我就借花献佛.在这里 重新分析下busybox是如何运行这个命令,同时又是如何调用到Linux内核中的mach_reset中的arch_reset,当针对不同的ARM 芯片时,作为Linux内核开发和驱动开发的朋友,对于这个流程还是一定要了解的。要不,出现问题,又如何找出问题呢。忘记了reboot的打印信息了, 如下:

The system is going down NOW !!  
Sending SIGTERM to all processes.  
Sending SIGKILL to all processes.  
Please stand by while rebooting the system.  
Restarting system.  

通过分析busybox1.20.0的代码可以看出在init.c中有这样一行的代码,如下:

 1     int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;  
 2     int init_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         /* struct sysinfo is linux-specific */  
12     #ifdef __linux__  
13         /* Make sure there is enough memory to do something useful. */  
14         if (ENABLE_SWAPONOFF) { //是否配置了swapoff命令  
15             struct sysinfo info;  
16       
17             if (sysinfo(&info) == 0  
18              && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024  
19             ) {  
20                 message(L_CONSOLE, "Low memory, forcing swapon");  
21                 /* swapon -a requires /proc typically */  
22                 new_init_action(SYSINIT, "mount -t proc proc /proc", "");  
23                 /* Try to turn on swap */  
24                 new_init_action(SYSINIT, "swapon -a", "");  
25                 run_actions(SYSINIT);   /* wait and removing */  
26             }  
27         }  
28     #endif  
29         ......  
30     /* Make the command line just say "init"  - thats all, nothing else */  
31         strncpy(argv[0], "init", strlen(argv[0]));  
32         /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */  
33         while (*++argv)  
34             memset(*argv, 0, strlen(*argv));  
35       
36         /* Set up signal handlers */  
37         /* Set up signal handlers */  
38         if (!DEBUG_INIT) {  
39             struct sigaction sa;  
40       
41             bb_signals(0  
42                 + (1 << SIGUSR1) /* halt */  
43                 + (1 << SIGTERM) /* reboot */  
44                 + (1 << SIGUSR2) /* poweroff */  
45                 , halt_reboot_pwoff);//看到这个halt_reboot_pwoff  
46             signal(SIGQUIT, restart_handler); /* re-exec another init */ //看到这个restart_handler函数,这是我们需要分析的  
47       
48             /* Stop handler must allow only SIGCONT inside itself */  
49             memset(&sa, 0, sizeof(sa));  
50             sigfillset(&sa.sa_mask);  
51             sigdelset(&sa.sa_mask, SIGCONT);  
52             sa.sa_handler = stop_handler;  
53             /* NB: sa_flags doesn't have SA_RESTART. 
54              * It must be able to interrupt wait(). 
55              */  
56             sigaction_set(SIGTSTP, &sa); /* pause */  
57             /* Does not work as intended, at least in 2.6.20. 
58              * SIGSTOP is simply ignored by init: 
59              */  
60             sigaction_set(SIGSTOP, &sa); /* pause */  
61       
62             /* SIGINT (Ctrl-Alt-Del) must interrupt wait(), 
63              * setting handler without SA_RESTART flag. 
64              */  
65             bb_signals_recursive_norestart((1 << SIGINT), record_signo);  
66         }  
67         ......  
68     }  

 

单独拿出halt_reboot_pwoff和restart_handler这个函数来看看

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

 

restart_handler函数如下:

 1     /* Handler for QUIT - exec "restart" action, 
 2      * else (no such action defined) do nothing */  
 3     static void restart_handler(int sig UNUSED_PARAM)  
 4     {  
 5         struct init_action *a;  
 6       
 7         for (a = init_action_list; a; a = a->next) {  
 8             if (!(a->action_type & RESTART))  
 9                 continue;  
10       
11             /* Starting from here, we won't return. 
12              * Thus don't need to worry about preserving errno 
13              * and such. 
14              */  
15       
16             reset_sighandlers_and_unblock_sigs();  
17       
18             run_shutdown_and_kill_processes();  
19       
20     #ifdef RB_ENABLE_CAD  
21             /* Allow Ctrl-Alt-Del to reboot the system. 
22              * This is how kernel sets it up for init, we follow suit. 
23              */  
24             reboot(RB_ENABLE_CAD); /* misnomer */  
25     #endif  
26       
27             if (open_stdio_to_tty(a->terminal)) {  
28                 dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);  
29                 /* Theoretically should be safe. 
30                  * But in practice, kernel bugs may leave 
31                  * unkillable processes, and wait() may block forever. 
32                  * Oh well. Hoping "new" init won't be too surprised 
33                  * by having children it didn't create. 
34                  */  
35                 //while (wait(NULL) > 0)  
36                 //  continue;  
37                 init_exec(a->command);  
38             }  
39             /* Open or exec failed */  
40             pause_and_low_level_reboot(RB_HALT_SYSTEM);  
41             /* not reached */  
42         }  
43     }  

 

通过分析,我们看到他们都会有调用这两个函数:reset_sighandlers_and_unblock_sigs();以及 run_shutdown_and_kill_processes();,我们重点关注如下这个函数:

 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     }  

 

嘿嘿,终于看到了上面的打印信息:The system is going down NOW !! 以及Sending SIGTERM to all processes. 同时在上面的halt_reboot_pwoff和restart_handler中都会调用这样一个函数,如下:

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

 

看到了吗?有一个reboot(magic)函数,对于vfork函数,请参考fork函数。这里不多说了.... 我们现在来看看reboot.h文件,如下:

 1     /* 
 2      * Definitions related to the reboot() system call, 
 3      * shared between init.c and halt.c. 
 4      */  
 5       
 6     #include <sys/reboot.h>  
 7       
 8     #ifndef RB_HALT_SYSTEM  
 9     # if defined(__linux__)  
10     #  define RB_HALT_SYSTEM  0xcdef0123  
11     #  define RB_ENABLE_CAD   0x89abcdef  
12     #  define RB_DISABLE_CAD  0  
13     #  define RB_POWER_OFF    0x4321fedc  
14     #  define RB_AUTOBOOT     0x01234567  
15     # elif defined(RB_HALT)  
16     #  define RB_HALT_SYSTEM  RB_HALT  
17     # endif  
18     #endif  
19       
20     /* Stop system and switch power off if possible.  */  
21     #ifndef RB_POWER_OFF  
22     # if defined(RB_POWERDOWN)  
23     #  define RB_POWER_OFF  RB_POWERDOWN  
24     # elif defined(__linux__)  
25     #  define RB_POWER_OFF  0x4321fedc  
26     # else  
27     #  warning "poweroff unsupported, using halt as fallback"  
28     #  define RB_POWER_OFF  RB_HALT_SYSTEM  
29     # endif  
30     #endif  

 

而在linux的内核中的定义如下:


busybox和linux内核中的REBOOT的定义值是一样的。看到了没有了。这个很重要的哦,否则busybox是无法调用linux内核的reboot函数。

 

二:Linux内核层的分析


Linux内核是如何衔接busybox的reboot函数的呢,如下代码:

  1. /* 
  2.  * Reboot system call: for obvious reasons only root may call it, 
  3.  * and even root needs to set up some magic numbers in the registers 
  4.  * so that some mistake won't make this reboot the whole machine. 
  5.  * You can also set the meaning of the ctrl-alt-del-key here. 
  6.  * 
  7.  * reboot doesn't sync: do that yourself before calling this. 
  8.  */  
  9. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  
  10.         void __user *, arg)  
  11. {  
  12.     char buffer[256];  
  13.     int ret = 0;  
  14.   
  15.     /* We only trust the superuser with rebooting the system. */  
  16.     if (!capable(CAP_SYS_BOOT))  
  17.         return -EPERM;  
  18.   
  19.     /* For safety, we require "magic" arguments. */  
  20.     if (magic1 != LINUX_REBOOT_MAGIC1 ||  
  21.         (magic2 != LINUX_REBOOT_MAGIC2 &&  
  22.                     magic2 != LINUX_REBOOT_MAGIC2A &&  
  23.             magic2 != LINUX_REBOOT_MAGIC2B &&  
  24.                     magic2 != LINUX_REBOOT_MAGIC2C))  
  25.         return -EINVAL;  
  26.   
  27.     /* Instead of trying to make the power_off code look like 
  28.      * halt when pm_power_off is not set do it the easy way. 
  29.      */  
  30.     if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  31.         cmd = LINUX_REBOOT_CMD_HALT;  
  32.   
  33.     lock_kernel();  
  34.     switch (cmd) {  
  35.     case LINUX_REBOOT_CMD_RESTART:  
  36.         kernel_restart(NULL); //这个就是重新启动Linx的命令  
  37.         break;  
  38.   
  39.     case LINUX_REBOOT_CMD_CAD_ON:  
  40.         C_A_D = 1;  
  41.         break;  
  42.   
  43.     case LINUX_REBOOT_CMD_CAD_OFF:  
  44.         C_A_D = 0;  
  45.         break;  
  46.   
  47.     case LINUX_REBOOT_CMD_HALT:  
  48.         kernel_halt();  
  49.         unlock_kernel();  
  50.         do_exit(0);  
  51.         panic("cannot halt");  
  52.   
  53.     case LINUX_REBOOT_CMD_POWER_OFF:  
  54.         kernel_power_off();  
  55.         unlock_kernel();  
  56.         do_exit(0);  
  57.         break;  
  58.   
  59.     case LINUX_REBOOT_CMD_RESTART2:  
  60.         if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {  
  61.             unlock_kernel();  
  62.             return -EFAULT;  
  63.         }  
  64.         buffer[sizeof(buffer) - 1] = '\0';  
  65.   
  66.         kernel_restart(buffer);  
  67.         break;  
  68.   
  69. #ifdef CONFIG_KEXEC  
  70.     case LINUX_REBOOT_CMD_KEXEC:  
  71.         ret = kernel_kexec();  
  72.         break;  
  73. #endif  
  74.   
  75. #ifdef CONFIG_HIBERNATION  
  76.     case LINUX_REBOOT_CMD_SW_SUSPEND:  
  77.         ret = hibernate();  
  78.         break;  
  79. #endif  
  80.   
  81.     default:  
  82.         ret = -EINVAL;  
  83.         break;  
  84.     }  
  85.     unlock_kernel();  
  86.     return ret;  
  87. }  


继续跟踪kernel_restart()函数,如下:

最终会调用一个machine_restart(cmd)函数,这个是跟具体的芯片有很大的关系的,我们进一步的分析如下:

看到了吗,最终是调用arch_reset来复位整个系统的。同时我们也看到了S3C2440的reset的函数如下:

在arm_pm_restart = s3c24xx_pm_restart()函数,最终也是调用arm_machine_restart(mod, cmd)来实现的。而在arm_machine_restart()函数中,最终也是调用arch_reset()函数来实现,而这个函数是在哪里呢。在 S3C2440没有看到arch_reset函数的实现,因此从S3C2410中找到了如下的代码,请继续看下面的代码:

       终于看到了arch_reset函数,最终是采用S3C2410或者S3C2440的WatchDog来实现reboot的命令的。大家可以想想,busybox的poweroff命令,是如何实现通过Linux系统关闭整个系统的电源呢,其实很简单,只需要实现下面的函数中的pm_power_off的回调函数即可。

       我们可以通过一个GPIO来控制整个系统的电源,而通过上面的pm_power_off的回调函数来实现,只需要在pm_power_off函数对GPIO进行操作就可以了。你看不是很简单吗?

 

posted @ 2016-08-29 19:42  dolinux  阅读(620)  评论(0)    收藏  举报