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

***************************************************************************************************************************
作者:EasyWave                                                                                 时间:2013.01.26

类别:Linux 内核系统源码分析                                                             声明:转载,请保留链接

注意:如有错误,欢迎指正。这些是我学习的日志文章......

***************************************************************************************************************************

一:Busyobx层的分析

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

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  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 **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进行操作就可以了。你看不是很简单吗?



转自:http://blog.csdn.net/wavemcu/article/details/8544333

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值