基于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文件,如下:
而在linux的内核中的定义如下:
busybox和linux内核中的REBOOT的定义值是一样的。看到了没有了。这个很重要的哦,否则busybox是无法调用linux内核的reboot函数。
二:Linux内核层的分析
Linux内核是如何衔接busybox的reboot函数的呢,如下代码:
- /*
- * Reboot system call: for obvious reasons only root may call it,
- * and even root needs to set up some magic numbers in the registers
- * so that some mistake won't make this reboot the whole machine.
- * You can also set the meaning of the ctrl-alt-del-key here.
- *
- * reboot doesn't sync: do that yourself before calling this.
- */
- SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
- void __user *, arg)
- {
- char buffer[256];
- int ret = 0;
- /* We only trust the superuser with rebooting the system. */
- if (!capable(CAP_SYS_BOOT))
- return -EPERM;
- /* For safety, we require "magic" arguments. */
- if (magic1 != LINUX_REBOOT_MAGIC1 ||
- (magic2 != LINUX_REBOOT_MAGIC2 &&
- magic2 != LINUX_REBOOT_MAGIC2A &&
- magic2 != LINUX_REBOOT_MAGIC2B &&
- magic2 != LINUX_REBOOT_MAGIC2C))
- return -EINVAL;
- /* Instead of trying to make the power_off code look like
- * halt when pm_power_off is not set do it the easy way.
- */
- if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
- cmd = LINUX_REBOOT_CMD_HALT;
- lock_kernel();
- switch (cmd) {
- case LINUX_REBOOT_CMD_RESTART:
- kernel_restart(NULL); //这个就是重新启动Linx的命令
- break;
- case LINUX_REBOOT_CMD_CAD_ON:
- C_A_D = 1;
- break;
- case LINUX_REBOOT_CMD_CAD_OFF:
- C_A_D = 0;
- break;
- case LINUX_REBOOT_CMD_HALT:
- kernel_halt();
- unlock_kernel();
- do_exit(0);
- panic("cannot halt");
- case LINUX_REBOOT_CMD_POWER_OFF:
- kernel_power_off();
- unlock_kernel();
- do_exit(0);
- break;
- case LINUX_REBOOT_CMD_RESTART2:
- if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
- unlock_kernel();
- return -EFAULT;
- }
- buffer[sizeof(buffer) - 1] = '\0';
- kernel_restart(buffer);
- break;
- #ifdef CONFIG_KEXEC
- case LINUX_REBOOT_CMD_KEXEC:
- ret = kernel_kexec();
- break;
- #endif
- #ifdef CONFIG_HIBERNATION
- case LINUX_REBOOT_CMD_SW_SUSPEND:
- ret = hibernate();
- break;
- #endif
- default:
- ret = -EINVAL;
- break;
- }
- unlock_kernel();
- return ret;
- }
继续跟踪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进行操作就可以了。你看不是很简单吗?
本文来自博客园,作者:dolinux,未经同意,禁止转载

浙公网安备 33010602011771号