您好,欢迎来到爱go旅游网。
搜索
您的当前位置:首页基于Linux与Busybox的Reboot命令流程分析

基于Linux与Busybox的Reboot命令流程分析

来源:爱go旅游网
基于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\

/* 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 #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_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进⾏操作就可以了。你看不是很简单吗?

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- igat.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务