***************************************************************************************************************************作者: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\
/* 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_pwoff
signal(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\ 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 */#endif
if (open_stdio_to_tty(a->terminal)) {
dbg_message(L_CONSOLE, \"Trying to re-exec %s\ /* 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\ sync(); sleep(1); kill(-1, SIGKILL);
message(L_CONSOLE, \"Sent SIG%s to all processes\ 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 # 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_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_KEXEC case LINUX_REBOOT_CMD_KEXEC: ret = kernel_kexec(); break;#endif #ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND: ret = hibernate(); break;#endif default: 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进⾏操作就可以了。你看不是很简单吗? 因篇幅问题不能全部显示,请点此查看更多更全内容