科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道分析内核初始化时根内存盘的加载过程

分析内核初始化时根内存盘的加载过程

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

当内核配置了内存盘时, 内核在初始化时可以将软盘加载到内存盘中作为根盘. 当同时配置了初始化内存盘(Initail RAM Disk)时, 内核在初始化时可以在安装主盘之前

作者:赛迪网技术社区 来源:赛迪网技术社区 2007年10月21日

关键字: 加载 初始化 内核 Linux

  • 评论
  • 分享微博
  • 分享邮件
 

当内核配置了内存盘时, 内核在初始化时可以将软盘加载到内存盘中作为根盘. 当同时配置了初始化内存盘(Initail RAM Disk)时, 内核在初始化时可以在安装主盘之前, 通过引导程序所加载的initrd文件建立一个内存初始化盘, 首先将它安装成根文件系统, 然后执行其根目录下的linuxrc 文件, 可用于在安装主盘之前加载一些内核模块. 等到linuxrc 程序退出后, 再将主盘安装成根文件系统, 并将内存初始化盘转移安装到其/initrd目录下。

当主盘就是initrd所生成的内存初始化盘时, 不再进行重新安装, 在DOS下用loadlin加载的抢救盘就是这种工作方式。

引导程序所加载的initrd为文件系统的映象文件, 可以是gzip压缩的, 也可以是不压缩的. 能够识别的文件系统有minix,ext2,romfs三种。

当内核的根盘为软盘时, 内核初始化时会测试软盘的指定部位是否存在文件系统或压缩文件映象, 然后将之加载或解压到内存盘中作为根盘. 这是单张抢救软盘的工作方式。

以下是有关代码:

   init/main.c 
  #ifdef CONFIG_BLK_DEV_INITRD 
  kdev_t real_root_dev; 启动参数所设定的根盘设备 
  #endif 
  asmlinkage void __init start_kernel(void) 
  { 
   char * command_line; 
   unsigned long mempages; 
   extern char saved_command_line[]; 
   lock_kernel(); 
   printk(linux_banner); 
   setup_arch(&command_line); arch/i386/kernel/setup.c中,
初始化initrd_start和initrd_end两个变量 
   ... 
  #ifdef CONFIG_BLK_DEV_INITRD 
   if (initrd_start && !initrd_below_start_ok && 
   initrd_start < min_low_pfn << PAGE_SHIFT) { 
   ; min_low_pfn为内核末端_end所开始的物理页号,initrd_start,initrd_end在rd.c中定义 
   printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " 
   "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT); 
   initrd_start = 0; 
   } 
  #endif 
   ... 
   kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); 创建init进程 
   unlock_kernel(); 
   current->need_resched = 1; 
   cpu_idle(); 
  } 
  static int init(void * unused) 
  { 
   lock_kernel(); 
   do_basic_setup(); 
   /* 
   * Ok, we have completed the initial bootup, and 
   * we're essentially up and running. Get rid of the 
   * initmem segments and start the user-mode stuff.. 
   */ 
   free_initmem(); 
   unlock_kernel(); 
   if (open("/dev/console", O_RDWR, 0) < 0) 
   printk("Warning: unable to open an initial console.\n"); 
   (void) dup(0); 
   (void) dup(0); 
   /* 
   * We try each of these until one succeeds. 
   * 
   * The Bourne shell can be used instead of init if we are 
   * trying to recover a really broken machine. 
   */ 
   if (execute_command) 
   execve(execute_command,argv_init,envp_init); 
   execve("/sbin/init",argv_init,envp_init); 
   execve("/etc/init",argv_init,envp_init); 
   execve("/bin/init",argv_init,envp_init); 
   execve("/bin/sh",argv_init,envp_init); 
   panic("No init found. Try passing init= option to kernel."); 
  } 
  static void __init do_basic_setup(void)
  { 
  #ifdef CONFIG_BLK_DEV_INITRD 
   int real_root_mountflags; 
  #endif 
   ... 
  #ifdef CONFIG_BLK_DEV_INITRD 
   real_root_dev = ROOT_DEV; ROOT_DEV为所请求根文件系统的块设备 
   real_root_mountflags = root_mountflags; 
   if (initrd_start && mount_initrd) root_mountflags &= ~MS_RDONLY; 
   else mount_initrd =0; 
  #endif
   start_context_thread(); 
   do_initcalls(); 会调用partition_setup()中加载内存盘 
   /* .. filesystems .. */ 
   filesystem_setup(); 
   /* Mount the root filesystem.. */ 
   mount_root(); 
   mount_devfs_fs (); 
  #ifdef CONFIG_BLK_DEV_INITRD 
   root_mountflags = real_root_mountflags; 
   if (mount_initrd && ROOT_DEV != real_root_dev 
   && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) { 
   ; 如果当前根盘为initrd所建立的内存盘 
   int error; 
   int i, pid; 
   pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); 
    创建新的任务去执行程序/linuxrc 
   if (pid>0) 
   while (pid != wait(&i)); 等待linuxrc进程退出 
   if (MAJOR(real_root_dev) != RAMDISK_MAJOR 
   || MINOR(real_root_dev) != 0) { 
   ; 如果原来的根盘不是0号内存盘,则使用原来的根文件系统, 
   ; 并且将内存盘转移到其/initrd目录下 
   error = change_root(real_root_dev,"/initrd"); 
   if (error) 
   printk(KERN_ERR "Change root to /initrd: " 
   "error %d\n",error); 
   } 
   } 
  #endif 
  } 
  #ifdef CONFIG_BLK_DEV_INITRD 
  static int do_linuxrc(void * shell) 
  { 
   static char *argv[] = { "linuxrc", NULL, }; 
  
   close(0);close(1);close(2); 
   setsid(); 设置新的session号 
   (void) open("/dev/console",O_RDWR,0); 
   (void) dup(0); 
   (void) dup(0); 
   return execve(shell, argv, envp_init); 
  } 
  #endif 
  ; arch/i386/kernel/setup.c 
  #define RAMDISK_IMAGE_START_MASK 0x07FF 
  #define RAMDISK_PROMPT_FLAG 0x8000 
  #define RAMDISK_LOAD_FLAG 0x4000 
  #define PARAM ((unsigned char *)empty_zero_page) 
  #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) 可用rdev设置的参数 
  #define LOADER_TYPE (*(unsigned char *) (PARAM+0x210)) 
  #define INITRD_START (*(unsigned long *) (PARAM+0x218)) 初始化盘映象起始物理地址 
  #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) 初始化盘字节数 
  
  void __init setup_arch(char **cmdline_p) 
  { 
   ... 
  #ifdef CONFIG_BLK_DEV_RAM 
   rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; 以块为单位 
   rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); 
   rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); 
  #endif 
   ... 
  #ifdef CONFIG_BLK_DEV_INITRD 
   if (LOADER_TYPE && INITRD_START) { 
   if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { 
   ; max_low_pfn表示内核空间1G范围以下最大允许的物理页号 
   reserve_bootmem(INITRD_START, INITRD_SIZE); 
   initrd_start = 
   INITRD_START ? INITRD_START + PAGE_OFFSET : 0; 转变为内核逻辑地址 
   initrd_end = initrd_start+INITRD_SIZE; 
   } 
   else { 
   printk("initrd extends beyond end of memory " 
   "(0x%08lx > 0x%08lx)\ndisabling initrd\n", 
   INITRD_START + INITRD_SIZE, 
   max_low_pfn << PAGE_SHIFT); 
   initrd_start = 0; 
   } 
   } 
  #endif 
   ... 
  } 
  ; fs/partitions/check.c: 
  int __init partition_setup(void) 
  { 
   device_init(); 包含ramdisk设备的初始化 
  #ifdef CONFIG_BLK_DEV_RAM 
  #ifdef CONFIG_BLK_DEV_INITRD 
   if (initrd_start && mount_initrd) initrd_load(); 
   ;如果启动时加载了initrd文件,则用它去初始化根内存盘 
   else 
  #endif 
   rd_load(); 如果内核配置了内存盘并且根盘指定为软盘则试图将软盘加载为根内存盘 
  #endif 
   return 0; 
  } 
  
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章