Linux内核关机重启源码分析

来源:互联网 发布:淘宝客服忙不忙 编辑:程序博客网 时间:2024/05/01 06:14

Linux在PC上的关机和重启可能由两种行为引发,一是通过用户编程,一是系统自己产生的消息。用户和系统进行交互的方式也有两个,一个是系统调用:sys_reboot,另一个就是apm或acpi的设备文件,通过对其操作也可以使系统关机或者重启。

 

一、从reboot命令开始

       reboot命令会执行系统调用来实现重启。我们在运行reboot时,会打印下面信息:

Restarting system.

这句话在kernel/sys.c的kernel_restart()函数中打印出来。

 

而调用kernel_restart函数的地方是,sys.c中的reboot系统调用宏定义中:

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,

              void __user *, arg)

{

……

       lock_kernel();

       switch (cmd) {

       case LINUX_REBOOT_CMD_RESTART:

              kernel_restart(NULL);

              break;

……

      

kernel_restart函数实现如下:

void kernel_restart(char *cmd)

{

       kernel_restart_prepare(cmd);  //重启前,向其它部分发出重启的消息

       if (!cmd)

              printk(KERN_EMERG "Restarting system.\n");

       else

              printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);

       machine_restart(cmd);   //实际重启

}

EXPORT_SYMBOL_GPL(kernel_restart);

 

对于X86平台的machine_restart实现在arch/x86/kernel/reboot.c中:

void machine_restart(char *cmd)

{

       machine_ops.restart(cmd);

}

 

      其中,machine_ops定义和初始化如下:

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

};

 

可见,restart函数是native_machine_restart,其实现如下:

static void native_machine_restart(char *__unused)

{

       printk("machine restart\n");

       if (!reboot_force)

              machine_shutdown();

       machine_emergency_restart();

}

      而native_machine_restart又调用了machine_emergency_restart函数,如下:

void machine_emergency_restart(void)

{

       machine_ops.emergency_restart();

}

 

      最终,实现X86重启的函数native_machine_emergency_restart如下:

static voidnative_machine_emergency_restart(void)

{

       int i;

       *((unsigned short *)__va(0x472)) = reboot_mode;

       for (;;) {

              /* Could also try the reset bit in the Hammer NB */

              switch (reboot_type) {

              case BOOT_KBD:

                     mach_reboot_fixups(); /* for board specific fixups */

                     for (i = 0; i < 10; i++) {

                            kb_wait();

                            udelay(50);

                            outb(0xfe, 0x64); /* pulse reset low */

                            udelay(50);

                     }

              case BOOT_TRIPLE:

                     load_idt(&no_idt);

                     __asm__ __volatile__("int3");

                     reboot_type = BOOT_KBD;

                     break;

 

#ifdef CONFIG_X86_32

              case BOOT_BIOS:

                     machine_real_restart(jump_to_bios, sizeof(jump_to_bios));

                     reboot_type = BOOT_KBD;

                     break;

#endif

              case BOOT_ACPI:

                     acpi_reboot();

                     reboot_type = BOOT_KBD;

                     break;

              case BOOT_EFI:

                     if (efi_enabled)

                            efi.reset_system(reboot_mode ? EFI_RESET_WARM : EFI_RESET_COLD,

                                           EFI_SUCCESS, 0, NULL);

                     reboot_type = BOOT_KBD;

                     break;

              }

       }

}

      一般情况下,运行reboot命令,进入的是BOOT_KBD这个case,然后,运行到BOOT_TRIPLE这个case,对于下面这句话,

__asm__ __volatile__("int3");

      本人找了N+1本书,发现这句话的作用是产生一个breakpoint异常。

      而实际重启的实现是在BOOT_KBD这个case中的:

                     for (i = 0; i < 10; i++) {

                            kb_wait();

                            udelay(50);

                            outb(0xfe, 0x64); /* pulse reset low */

                            udelay(50);

                     }

      

      对于outb(0xfe,0x64)原理,我也不清楚,ICH8芯片手册找了没找到,Intel 965北桥芯片手册也没找到,网上说:

在不通过bios进行重启的情况下,系统向端口0xfe写入数字0x64,这种重启的具体原理我还不大清楚,似乎是模拟了一次reset键的按下。

      知道的XDJM们在评论中告知一声,谢谢。

 

二、APM和ACPI

acpi模块的相关源代码在linux/drivers/acpi/中,以后有时间将对其进行具体分析。

 

注意:本文基于linux-2.6.28和X86平台进行分析。

原创粉丝点击