基于Linux与Busybox的Reboot命令流程分析
***************************************************************************************************************************
作者: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进行操作就可以了。你看不是很简单吗?