一:Busyobx层的分析

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


1. The system is going down NOW !!  
2. Sending SIGTERM to all processes.  
3. Sending SIGKILL to all processes.  
4. Please stand by while rebooting the system.  
5. Restarting system.  
6. .



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


1. int init_main(int argc, char
2. int init_main(int argc UNUSED_PARAM, char
3. {  
4. static const int
5.         RB_HALT_SYSTEM,  
6.         RB_POWER_OFF,  
7.         RB_AUTOBOOT  
8.     };  
9. static const
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
16.   
17. if
18. long long)info.totalram < 1024*1024  
19.         ) {  
20. "Low memory, forcing swapon");  
21. /* swapon -a requires /proc typically */
22. "mount -t proc proc /proc", "");  
23. /* Try to turn on swap */
24. "swapon -a", "");  
25. /* wait and removing */
26.         }  
27.     }  
28. #endif
29.     ......  
30. /* Make the command line just say "init"  - thats all, nothing else */
31. "init", strlen(argv[0]));  
32. /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
33. while
34.         memset(*argv, 0, strlen(*argv));  
35.   
36. /* Set up signal handlers */
37. /* Set up signal handlers */
38. if
39. struct
40.   
41.         bb_signals(0  
42. /* halt */
43. /* reboot */
44. /* poweroff */
45. //看到这个halt_reboot_pwoff
46. /* re-exec another init */ //看到这个restart_handler函数,这是我们需要分析的
47.   
48. /* Stop handler must allow only SIGCONT inside itself */
49. 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. /* pause */
57. /* Does not work as intended, at least in 2.6.20.
58.          * SIGSTOP is simply ignored by init:
59.          */
60. /* 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
3. static void halt_reboot_pwoff(int
4. {  
5. const char
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. "halt";  
19.     rb = RB_HALT_SYSTEM;  
20. if
21. "reboot";  
22.         rb = RB_AUTOBOOT;  
23. else if
24. "poweroff";  
25.         rb = RB_POWER_OFF;  
26.     }  
27. "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
4. {  
5. struct
6.   
7. for
8. if
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. /* misnomer */
25. #endif
26.   
27. if
28. "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. "The system is going down NOW!");  
9.   
10. /* Send signals to every process _except_ pid 1 */
11.     kill(-1, SIGTERM);  
12. "Sent SIG%s to all processes", "TERM");  
13.     sync();  
14.     sleep(1);  
15.   
16.     kill(-1, SIGKILL);  
17. "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
2. static void
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
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的内核中的定义如下:

linux系统reboot 之后一直连不上 linux reboot无法重启_#ifndef

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
11. {  
12. char
13. int
14.   
15. /* We only trust the superuser with rebooting the system. */
16. if
17. return
18.   
19. /* For safety, we require "magic" arguments. */
20. if
21.         (magic2 != LINUX_REBOOT_MAGIC2 &&  
22.                     magic2 != LINUX_REBOOT_MAGIC2A &&  
23.             magic2 != LINUX_REBOOT_MAGIC2B &&  
24.                     magic2 != LINUX_REBOOT_MAGIC2C))  
25. return
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
31.         cmd = LINUX_REBOOT_CMD_HALT;  
32.   
33.     lock_kernel();  
34. switch
35. case
36. //这个就是重新启动Linx的命令
37. break;  
38.   
39. case
40.         C_A_D = 1;  
41. break;  
42.   
43. case
44.         C_A_D = 0;  
45. break;  
46.   
47. case
48.         kernel_halt();  
49.         unlock_kernel();  
50.         do_exit(0);  
51. "cannot halt");  
52.   
53. case
54.         kernel_power_off();  
55.         unlock_kernel();  
56.         do_exit(0);  
57. break;  
58.   
59. case
60. if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {  
61.             unlock_kernel();  
62. return
63.         }  
64. sizeof(buffer) - 1] = '\0';  
65.   
66.         kernel_restart(buffer);  
67. break;  
68.   
69. #ifdef CONFIG_KEXEC
70. case
71.         ret = kernel_kexec();  
72. break;  
73. #endif
74.   
75. #ifdef CONFIG_HIBERNATION
76. case
77.         ret = hibernate();  
78. break;  
79. #endif
80.   
81. default:  
82.         ret = -EINVAL;  
83. break;  
84.     }  
85.     unlock_kernel();  
86. return
87. }


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


linux系统reboot 之后一直连不上 linux reboot无法重启_Linux  busybox_02

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


linux系统reboot 之后一直连不上 linux reboot无法重启_#endif_03


linux系统reboot 之后一直连不上 linux reboot无法重启_linux_04


linux系统reboot 之后一直连不上 linux reboot无法重启_reboot 重启流程解析_05

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


linux系统reboot 之后一直连不上 linux reboot无法重启_linux_06

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


linux系统reboot 之后一直连不上 linux reboot无法重启_#ifndef_07

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


linux系统reboot 之后一直连不上 linux reboot无法重启_#ifndef_08

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