偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.
全部博文(1747)
分类: LINUX
2011-12-02 09:23:38
BusyBox 版本1.10-2
用户在console下输入reboot命令,
busybox会调用到halt_main
int halt_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
.....
/* Perform action. */
if (ENABLE_INIT && !(flags & 4)) {
if (ENABLE_FEATURE_INITRD) {
pid_t *pidlist = find_pid_by_name("linuxrc");
if (pidlist[0] > 0)
rc = kill(pidlist[0], signals[which]);
if (ENABLE_FEATURE_CLEAN_UP)
free(pidlist);
}
if (rc)
rc = kill(1, signals[which]); //如果INIT 进程有,就发信号给它
} else
rc = reboot(magic[which]); //否则直接调用kernel的reboot
if (rc)
bb_error_msg("no");
return rc;
}
在init.c/init_main中(这是INIT被config 以后才会使用的函数,也就是busybox默认的init)
int init_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct init_action *a;
pid_t wpid;
......
bb_signals(0
+ (1 << SIGUSR1) /* halt */
+ (1 << SIGUSR2) /* poweroff */
+ (1 << SIGTERM) /* reboot */
, halt_reboot_pwoff); //挂载reboot,halt,poweroff 信号处理的handler
.....
}
所以此时当halt_main中发信号要reboot以后,系统进入halt_reboot_pwoff
static void halt_reboot_pwoff(int sig)
{
const char *m;
int rb;
kill_all_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 | L_LOG, "Requesting system %s", m);
/* allow time for last message to reach serial console */
sleep(2);
init_reboot(rb); //真正调用kernel的重启动
loop_forever();
}
static void kill_all_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); //调用run_action ,它会parse inittab中的action去做一些事情
/* first disable all our signals */
sigprocmask_allsigs(SIG_BLOCK);
message(L_CONSOLE | L_LOG, "The system is going down NOW!");
/* Allow Ctrl-Alt-Del to reboot system. */
init_reboot(RB_ENABLE_CAD); //告诉系统可以用CAD来重启
/* Send signals to every process _except_ pid 1 */
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM");
kill(-1, SIGTERM); //发送终止信号
sync(); //刷cache回设备
sleep(1);
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL");
kill(-1, SIGKILL); //杀死进程
sync();
sleep(1);
}
/* Run all commands of a particular type */
static void run_actions(int action_type)
{
struct init_action *a, *tmp;
for (a = init_action_list; a; a = tmp) {
tmp = a->next;
if (a->action_type & action_type) {
// Pointless: run() will error out if open of device fails.
///* a->terminal of "" means "init's console" */
//if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
// //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/);
// delete_init_action(a);
//} else
if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
waitfor(run(a)); //做事的是run函数
delete_init_action(a);
} else if (a->action_type & ONCE) {
run(a);
delete_init_action(a);
} else if (a->action_type & (RESPAWN | ASKFIRST)) {
/* Only run stuff with pid==0. If they have
* a pid, that means it is still running */
if (a->pid == 0) {
a->pid = run(a);
}
}
}
}
}
static pid_t run(const struct init_action *a)
{
…
init_exec(a->command); //真正执行命令, 这个函数应该会使用之前parse inittab 中产生的shutddown action
…
}
回头来看 init_reboot
先看下magic 定义
RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */
RB_ENABLE_CAD = 0x89abcdef,
RB_DISABLE_CAD = 0,
RB_POWER_OFF = 0x4321fedc,
RB_AUTOBOOT = 0x01234567,
对比linux kernel的看看,一致
#define LINUX_REBOOT_CMD_RESTART 0x01234567
#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
#define LINUX_REBOOT_CMD_KEXEC 0x45584543
static void init_reboot(unsigned long magic)
{
pid_t pid;
/* We have to fork here, since the kernel calls do_exit(0) in
* linux/kernel/sys.c, which can cause the machine to panic when
* the init process is killed.... */
pid = vfork();
if (pid == 0) { /* child */
reboot(magic); //最重要的事情,调用reboot
_exit(0);
}
waitfor(pid);
}
Busybox的调用顺序看完了,我们来看看linux reboot做了哪些事情
Linux2.6.28 kernel(是不是有点老,手边就这个最方便) sys.c/sys_reboot
Sys_reboot 是reboot的系统调用,自然我们看这个
asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg)
{
…
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL); //系统重启
break;
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1; //允许用户ctrl_alt_del重启
break;
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
unlock_kernel();
do_exit(0);
break;
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
unlock_kernel();
do_exit(0);
break;
….
}
系统重启:
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd); //这个函数中kernel/和设备驱动等需要做重启前的准备工作
if (!cmd) {
printk(KERN_EMERG "Restarting system./n");
} else {
printk(KERN_EMERG "Restarting system with command '%s'./n", cmd);
}
printk("./n");
machine_restart(cmd);
}
kernel 的重启准备
void kernel_restart_prepare(char *cmd)
{
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); //调用reboot 的notifier 链表,很多必要的设备,驱动都会注册一个reboot_notifier,这样在内核准备重启时还有机会把设备最后需要做的事情完成
system_state = SYSTEM_RESTART;
device_shutdown();
sysdev_shutdown();
}
设备驱动会调用这个函数来注册reboot notifier:
/**
* register_reboot_notifier - Register function to be called at reboot time
* @nb: Info about notifier function to be called
*
* Registers a function with the list of functions
* to be called at reboot time.
*
* Currently always returns zero, as blocking_notifier_chain_register()
* always returns zero.
*/
int register_reboot_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&reboot_notifier_list, nb);
}
看看MTD 设备挂的接口:
struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
{
.....
mtd->priv = map;
mtd->type = MTD_NORFLASH;
/* Fill in the default mtd operations */
mtd->erase = cfi_intelext_erase_varsize;
mtd->read = cfi_intelext_read;
mtd->write = cfi_intelext_write_words;
mtd->sync = cfi_intelext_sync;
mtd->lock = cfi_intelext_lock;
mtd->unlock = cfi_intelext_unlock;
mtd->suspend = cfi_intelext_suspend;
mtd->resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->writesize = 1;
mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
....
}
static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
{
...
register_reboot_notifier(&mtd->reboot_notifier);
....
return NULL;
}
再看看这个函数,如果是bus就调用bus的shutdown,如果设备就调用设备的。
/**
* device_shutdown - call ->shutdown() on each device to shutdown.
*/
void device_shutdown(void)
{
struct device *dev, *devn;
list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list,
kobj.entry) {
if (dev->bus && dev->bus->shutdown) {
dev_dbg(dev, "shutdown/n");
dev->bus->shutdown(dev);
} else if (dev->driver && dev->driver->shutdown) {
dev_dbg(dev, "shutdown/n");
dev->driver->shutdown(dev);
}
}
kobject_put(sysfs_dev_char_kobj);
kobject_put(sysfs_dev_block_kobj);
kobject_put(dev_kobj);
}
其他看看吧
void machine_restart(char *cmd)
{
machine_ops.restart(cmd);
}
struct machine_ops machine_ops = {
.power_off = native_machine_power_off,
.shutdown = native_machine_shutdown,
.emergency_restart = native_machine_emergency_restart,
.restart = native_machine_restart,
.halt = native_machine_halt,
#ifdef CONFIG_KEXEC
.crash_shutdown = native_machine_crash_shutdown,
#endif
};
static void native_machine_restart(char *__unused)
{
printk("machine restart/n");
if (!reboot_force)
machine_shutdown(); //à最后指向native_machine_shutdown
machine_emergency_restart(); //->最后指向native_machine_emergency_restart
}
native_machine_emergency_restart 中定义了集中reboot的的方式
方式如下,它们由reboot_setup设置,用户也可以传入,default by KBD。
/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old]
warm Don't set the cold reboot flag
cold Set the cold reboot flag
bios Reboot by jumping through the BIOS (only for X86_32)
smp Reboot by executing reset on BSP or other CPU (only for X86_32)
triple Force a triple fault (init)
kbd Use the keyboard controller. cold reset (default)
acpi Use the RESET_REG in the FADT
efi Use efi reset_system runtime service
force Avoid anything that could hang.
*/
Reboot_setup由Setup.c/setup_arch调用,
顺便分析下native_machine_emergency_restart
native_machine_emergency_restart
{
…
For (;;){
..
switch (reboot_type) {
case BOOT_KBD:
... //使用keyboard 8042去reboot硬件
case BOOT_TRIPLE:
case BOOT_BIOS://bios boot
case BOOT_ACPI://使用ACPI table提供的方法来boot
case BOOT_EFI://使用EFI 的runtime service reset 来reset 系统
}
}