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

來源:互聯網
上載者:User

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

類別:Linux 核心系統源碼分析                                                             聲明:轉載,請保留連結

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

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

一: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中有這樣一行的代碼,如下:

int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;int init_main(int argc UNUSED_PARAM, char **argv){static const int magic[] = {RB_HALT_SYSTEM,RB_POWER_OFF,RB_AUTOBOOT};static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };....../* struct sysinfo is linux-specific */#ifdef __linux__/* Make sure there is enough memory to do something useful. */if (ENABLE_SWAPONOFF) { //是否配置了swapoff命令struct sysinfo info;if (sysinfo(&info) == 0 && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024) {message(L_CONSOLE, "Low memory, forcing swapon");/* swapon -a requires /proc typically */new_init_action(SYSINIT, "mount -t proc proc /proc", "");/* Try to turn on swap */new_init_action(SYSINIT, "swapon -a", "");run_actions(SYSINIT);   /* wait and removing */}}#endif....../* Make the command line just say "init"  - thats all, nothing else */strncpy(argv[0], "init", strlen(argv[0]));/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */while (*++argv)memset(*argv, 0, strlen(*argv));/* Set up signal handlers *//* Set up signal handlers */if (!DEBUG_INIT) {struct sigaction sa;bb_signals(0+ (1 << SIGUSR1) /* halt */+ (1 << SIGTERM) /* reboot */+ (1 << SIGUSR2) /* poweroff */, halt_reboot_pwoff);//看到這個halt_reboot_pwoffsignal(SIGQUIT, restart_handler); /* re-exec another init */ //看到這個restart_handler函數,這是我們需要分析的/* Stop handler must allow only SIGCONT inside itself */memset(&sa, 0, sizeof(sa));sigfillset(&sa.sa_mask);sigdelset(&sa.sa_mask, SIGCONT);sa.sa_handler = stop_handler;/* NB: sa_flags doesn't have SA_RESTART. * It must be able to interrupt wait(). */sigaction_set(SIGTSTP, &sa); /* pause *//* Does not work as intended, at least in 2.6.20. * SIGSTOP is simply ignored by init: */sigaction_set(SIGSTOP, &sa); /* pause *//* SIGINT (Ctrl-Alt-Del) must interrupt wait(), * setting handler without SA_RESTART flag. */bb_signals_recursive_norestart((1 << SIGINT), record_signo);}......}

單獨拿出halt_reboot_pwoff和restart_handler這個函數來看看

/* The SIGUSR[12]/SIGTERM handler */static void halt_reboot_pwoff(int sig) NORETURN;static void halt_reboot_pwoff(int sig){const char *m;unsigned rb;/* We may call run() and it unmasks signals, * including the one masked inside this signal handler. * Testcase which would start multiple reboot scripts: *  while true; do reboot; done * Preventing it: */reset_sighandlers_and_unblock_sigs();run_shutdown_and_kill_processes();m = "halt";rb = RB_HALT_SYSTEM;if (sig == SIGTERM) {m = "reboot";rb = RB_AUTOBOOT;} else if (sig == SIGUSR2) {m = "poweroff";rb = RB_POWER_OFF;}message(L_CONSOLE, "Requesting system %s", m);pause_and_low_level_reboot(rb);/* not reached */}


restart_handler函數如下:

/* Handler for QUIT - exec "restart" action, * else (no such action defined) do nothing */static void restart_handler(int sig UNUSED_PARAM){struct init_action *a;for (a = init_action_list; a; a = a->next) {if (!(a->action_type & RESTART))continue;/* Starting from here, we won't return. * Thus don't need to worry about preserving errno * and such. */reset_sighandlers_and_unblock_sigs();run_shutdown_and_kill_processes();#ifdef RB_ENABLE_CAD/* Allow Ctrl-Alt-Del to reboot the system. * This is how kernel sets it up for init, we follow suit. */reboot(RB_ENABLE_CAD); /* misnomer */#endifif (open_stdio_to_tty(a->terminal)) {dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);/* Theoretically should be safe. * But in practice, kernel bugs may leave * unkillable processes, and wait() may block forever. * Oh well. Hoping "new" init won't be too surprised * by having children it didn't create. *///while (wait(NULL) > 0)//continue;init_exec(a->command);}/* Open or exec failed */pause_and_low_level_reboot(RB_HALT_SYSTEM);/* not reached */}}


通過分析,我們看到他們都會有調用這兩個函數:reset_sighandlers_and_unblock_sigs();以及 run_shutdown_and_kill_processes();,我們重點關注如下這個函數:

static void run_shutdown_and_kill_processes(void){/* Run everything to be run at "shutdown".  This is done _prior_ * to killing everything, in case people wish to use scripts to * shut things down gracefully... */run_actions(SHUTDOWN);message(L_CONSOLE | L_LOG, "The system is going down NOW!");/* Send signals to every process _except_ pid 1 */kill(-1, SIGTERM);message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");sync();sleep(1);kill(-1, SIGKILL);message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");sync();/*sleep(1); - callers take care about making a pause */}


嘿嘿,終於看到了上面的列印資訊:The system is going down NOW !! 以及Sending SIGTERM to all processes. 同時在上面的halt_reboot_pwoff和restart_handler中都會調用這樣一個函數,如下:

static void pause_and_low_level_reboot(unsigned magic) NORETURN;static void pause_and_low_level_reboot(unsigned magic){pid_t pid;/* Allow time for last message to reach serial console, etc */sleep(1);/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) * in linux/kernel/sys.c, which can cause the machine to panic when * the init process exits... */pid = vfork();if (pid == 0) { /* child */reboot(magic);_exit(EXIT_SUCCESS);}while (1)sleep(1);}


看到了嗎?有一個reboot(magic)函數,對於vfork函數,請參考fork函數。這裡不多說了.... 我們現在來看看reboot.h檔案,如下:

/* * Definitions related to the reboot() system call, * shared between init.c and halt.c. */#include <sys/reboot.h>#ifndef RB_HALT_SYSTEM# if defined(__linux__)#  define RB_HALT_SYSTEM  0xcdef0123#  define RB_ENABLE_CAD   0x89abcdef#  define RB_DISABLE_CAD  0#  define RB_POWER_OFF    0x4321fedc#  define RB_AUTOBOOT     0x01234567# elif defined(RB_HALT)#  define RB_HALT_SYSTEM  RB_HALT# endif#endif/* Stop system and switch power off if possible.  */#ifndef RB_POWER_OFF# if defined(RB_POWERDOWN)#  define RB_POWER_OFF  RB_POWERDOWN# elif defined(__linux__)#  define RB_POWER_OFF  0x4321fedc# else#  warning "poweroff unsupported, using halt as fallback"#  define RB_POWER_OFF  RB_HALT_SYSTEM# endif#endif

而在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_KEXECcase LINUX_REBOOT_CMD_KEXEC:ret = kernel_kexec();break;#endif#ifdef CONFIG_HIBERNATIONcase LINUX_REBOOT_CMD_SW_SUSPEND:ret = hibernate();break;#endifdefault: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進行操作就可以了。你看不是很簡單嗎?

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.