一: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?<
+?(1?<
+?(1?<
,?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?<
}??
......??
}??
單獨拿出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進行操作就可以了。你看不是很簡單嗎?
?
評論
查看更多