***************************************************************************************************************************
作者: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進行操作就可以了。你看不是很簡單嗎?