基於Linux與Busybox的Reboot命令流程分析

***************************************************************************************************************************
作者:EasyWave                                                                                 時間:2013.01.26

類別:Linux 內核系統源碼分析                                                             聲明:轉載,請保留鏈接

注意:如有錯誤,歡迎指正。這些是我學習的日誌文章......

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

一: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 **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

發佈了113 篇原創文章 · 獲贊 329 · 訪問量 44萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章