×
嵌入式 > 嵌入式开发 > 详情

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

发布时间:2020-05-16 发布时间:
|

一:Busyobx层的分析

这段时间,在忙到一个项目时,需要在busybox中用到reboot命令,开始在busybox中的shell中输入reboot命令,始终如下的信息,然后就停止在那里了,无法重启...为了彻底的弄明白这个问题,我在网络上找了很久,终于有个人写的一个reboot流程分析,我就借花献佛.在这里重新分析下busybox是如何运行这个命令,同时又是如何调用到Linux内核中的mach_reset中的arch_reset,当针对不同的ARM芯片时,作为Linux内核开发和驱动开发的朋友,对于这个流程还是一定要了解的。要不,出现问题,又如何找出问题呢。忘记了reboot的打印信息了,如下:

[plain] view plain copy

print?

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中有这样一行的代码,如下:

[cpp] view plain copy

print?

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_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这个函数来看看

[cpp] view plain copy

print?

/* 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函数如下:

[cpp] view plain copy

print?

/* 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", 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();,我们重点关注如下这个函数:

[cpp] view plain copy

print?

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中都会调用这样一个函数,如下:

[cpp] view plain copy

print?

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文件,如下:

[cpp] view plain copy

print?

/* 

* 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函数的呢,如下代码:

[cpp] view plain copy

print?

/* 

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



 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
RIOS实验室联手Imagination.共同助力RISC-V生态发展