首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

x86平台linux系统重启流程跟踪

x86平台linux系统重启流程跟踪

一直以来,笔者只知道重启Linux系统性使用reboot,但对其过程却无所知,涉及到哪些知识点也无概念。本文就跟踪一下重启的流程,平台为Intel x86,Linux内核版本为3.17。行文中“重启”与“复位”等价。
一、初识在Linux命令行下输入reboot,终端出现如下信息:




  • * Stopping web server apache2         *   
  • Stop in my script and clean net.rules ....  
  • * Asking all remaining proc[ OK ]to terminate...         
  • [ OK ] * All processes ended within 1 seconds...         
  • rpcbind: rpcbind terminating on signal. Restart with "rpcbind -w"
  • [ OK ]ctivating swap...         
  • [ OK ] * Unmounting local filesystems...         
  • * Will now restart  
  • [  847.054796] reboot: Restarting system  

信息格式错乱,但不影响分析。系统首先做的是停止apache2,然后执行用户自定义脚本。再结束进程、卸载文件系统。最后提示“reboot: Restarting system”,便完成使命,系统重启。从上面信息第二行看到执行了笔者自己写的脚本,它的作用是用于清除70-persistent-net.rules文件,主要解决当时的一个棘手问题,离今二年有余,由是怀念。
二、用户空间本节抽取uClinux和busybox源码中关于重启部分函数代码,以便了解用户空间重启的过程。两者的重启代码具备一定代表。
先看一下uClinux重启代码片段:




  • int main(int argc, char *argv[])  
  • {  
  •     kill(1, SIGTSTP);  
  •     sync();  
  •     signal(SIGTERM,SIG_IGN);  
  •     setpgrp();  
  •     kill(-1, SIGTERM);  
  •     kill(-1, SIGHUP);  
  •     sleep(1);  
  •     kill(-1, SIGKILL);  
  •     sync();  
  •     sleep(1);  
  • #if __GNU_LIBRARY__ > 5
  •     reboot(0x01234567);  
  • #else
  •     reboot(0xfee1dead, 672274793, 0x01234567);  
  • #endif
  •     exit(0); /* Shrug */
  • }  

  • extern
    int bb_shutdown_system(unsigned long magic)  
  • {  
  •     int pri = LOG_KERN|LOG_NOTICE|LOG_FACMASK;  
  •     const
    char *message;  

  •     /* Don't kill ourself */
  •     signal(SIGTERM,SIG_IGN);  
  •     signal(SIGHUP,SIG_IGN);  
  •     setpgrp();  

  •     /* Allow Ctrl-Alt-Del to reboot system. */
  • #ifndef RB_ENABLE_CAD
  • #define RB_ENABLE_CAD   0x89abcdef
  • #endif
  •     reboot(RB_ENABLE_CAD);  

  •     openlog(bb_applet_name, 0, pri);  

  •     message = "\nThe system is going down NOW !!";  
  •     syslog(pri, "%s", message);  
  •     printf(bb_shutdown_format, message);  

  •     sync();  

  •     /* Send signals to every process _except_ pid 1 */
  •     message = "Sending SIGTERM to all processes.";  
  •     syslog(pri, "%s", message);  
  •     printf(bb_shutdown_format, message);  

  •     kill(-1, SIGTERM);  
  •     sleep(1);  
  •     sync();  

  •     message = "Sending SIGKILL to all processes.";  
  •     syslog(pri, "%s", message);  
  •     printf(bb_shutdown_format, message);  

  •     kill(-1, SIGKILL);  
  •     sleep(1);  

  •     sync();  

  •     reboot(magic);  
  •     return 0; /* Shrug */
  • }  

两者处理过程类似,首先调用kill发送信号,最后调用reboot函数。
reboot是一个系统调用,man手册说明如下:




  • /* For libc4 and libc5 the library call and the system call
  •    are identical, and since kernel version 2.1.30 there are
  •    symbolic names LINUX_REBOOT_* for the constants and a
  •    fourth argument to the call: */

  • #include <unistd.h>
  • #include <linux/reboot.h>

  • int reboot(int magic, int magic2, int cmd, void *arg);  

  • /* Under glibc some of the constants involved have gotten
  •    symbolic names RB_*, and the library call is a 1-argument
  •    wrapper around the 3-argument system call: */

  • #include <unistd.h>
  • #include <sys/reboot.h>

  • int reboot(int cmd);  

如果不考虑影响其它服务、进程的话,直接在代码调用reboot即可完成系统重启功能。
三、内核空间在了解用户空间的执行过程后,再看看内核空间是如何实现的。
前节提到reboot是一个系统调用。其定义位于kernel/reboot.c文件,如下:




  • SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  
  •           void __user *, arg)  
  • {  
  •      mutex_lock(&reboot_mutex);  
  •      switch (cmd) {  
  •      case LINUX_REBOOT_CMD_RESTART:  
  •           kernel_restart(NULL);  
  •           break;  

  •      case LINUX_REBOOT_CMD_HALT:  
  •           kernel_halt();  
  •           do_exit(0);  
  •           panic("cannot halt");  

  •      case LINUX_REBOOT_CMD_POWER_OFF:  
  •           kernel_power_off();  
  •           do_exit(0);  
  •           break;  

  •      default:  
  •           ret = -EINVAL;  
  •           break;  
  •      }  
  •      mutex_unlock(&reboot_mutex);  
  •      return ret;  
  • }  

reboot进行LINUX_REBOOT_CMD_RESTART分支,调用的函数为kernel_restart。在终端看到的字符串“Restarting system”就是在里面打印的。它的实现如下(同样位于kernel/reboot.c文件):




  • void kernel_restart(char *cmd)  
  • {  
  •      kernel_restart_prepare(cmd);  
  •      migrate_to_reboot_cpu();  
  •      syscore_shutdown();  
  •      if (!cmd)  
  •           pr_emerg("Restarting system\n");  
  •      else
  •           pr_emerg("Restarting system with command '%s'\n", cmd);  
  •      kmsg_dump(KMSG_DUMP_RESTART);  
  •      machine_restart(cmd);  
  • }  
  • EXPORT_SYMBOL_GPL(kernel_restart);  


函数最后调用machine_restart。接着看一下machine_restart实现(位于arch/x86/kernel/reboot.c):




  • void machine_restart(char *cmd)  
  • {  
  •      machine_ops.restart(cmd);  
  • }  

该函数调用了machine_ops结构体的函数指针,看一下machine_ops结构体定义(位于arch/x86/kernel/reboot.c):





  • 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)  
  • {  
  •      pr_notice("machine restart\n");  

  •      if (!reboot_force)  
  •           machine_shutdown();  
  •      __machine_emergency_restart(0);  
  • }  

继续看调用的函数__machine_emergency_restart:





  • static
    void __machine_emergency_restart(int emergency)  
  • {  
  •      reboot_emergency = emergency;  
  •      machine_ops.emergency_restart();  
  • }  

而machine_ops.emergency_restart函数实际为native_machine_emergency_restart。
最终,重启实现的函数为native_machine_emergency_restar(位于arch/x86/kernel/reboot.c):




  • static
    void native_machine_emergency_restart(void)  
  • {  
  •      for (;;) {  
  •           /* Could also try the reset bit in the Hammer NB */
  •           switch (reboot_type) { // 重启标志:reboot_type。
  •           case BOOT_ACPI:  
  •                acpi_reboot();  
  •                reboot_type = BOOT_KBD; // BOOT_ACPI不成功再到BOOT_KBD
  •                break;  


  •           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 */
  •                     outb(0x0e, 0xcf9); /* for byatrail e3800 SOC by Late Lee*/
  •                     udelay(50);  
  •                }  
  •                if (attempt == 0 && orig_reboot_type == BOOT_ACPI) {  
  •                     attempt = 1;  
  •                     reboot_type = BOOT_ACPI;  
  •                } else {  
  •                     reboot_type = BOOT_EFI; // BOOT_KBD不成功再到BOOT_EFI
  •                }  
  •                break;  


  •           case BOOT_EFI:  
  •                efi_reboot(reboot_mode, NULL);  
  •                reboot_type = BOOT_BIOS; // BOOT_EFI不成功再到BOOT_BIOS
  •                break;  


  •           case BOOT_BIOS:  
  •                machine_real_restart(MRR_BIOS);  


  •                /* We're probably dead after this, but... */
  •                reboot_type = BOOT_CF9_SAFE;// BOOT_BIOS不成功再到BOOT_CF9_SAFE
  •                break;  


  •           case BOOT_CF9_FORCE:  
  •                port_cf9_safe = true;  
  •                /* Fall through */


  •           case BOOT_CF9_SAFE:  
  •                if (port_cf9_safe) {  
  •                     u8 reboot_code = reboot_mode == REBOOT_WARM ?  0x06 : 0x0E;  
  •                     u8 cf9 = inb(0xcf9) & ~reboot_code;  
  •                     outb(cf9|2, 0xcf9); /* Request hard reset */
  •                     udelay(50);  
  •                     /* Actually do the reset */
  •                     outb(cf9|reboot_code, 0xcf9);  
  •                     udelay(50);  
  •                }  
  •                reboot_type = BOOT_TRIPLE; // BOOT_CF9_SAFE不成功再到BOOT_TRIPLE
  •                break;  


  •           case BOOT_TRIPLE:  
  •                load_idt(&no_idt);  
  •                __asm__ __volatile__("int3");  


  •                /* We're probably dead after this, but... */
  •                reboot_type = BOOT_KBD;  
  •                break;  
  •           }  
  •      }  
  • }  
返回列表