热门关键字:  ubuntu  分区  Fedora  linux系统进程  函数

当前位置 :| 主页>Linux教程>内核研究>

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

来源: 作者: 时间:2007-04-07 Tag: 点击:

当内核配置了内存盘时, 内核在初始化时可以将软盘加载到内存盘中作为根盘. 当同时配置了初始化内存盘(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; 
  } 
  



相关文章:
精通initramfs构建step by step
Linux利用kexec迅速切换内核
进程上下文VS中断上下文
内核通知链 学习笔记
linux spi子系统驱动分析
menuconfig 配置选项
《Linux操作系统内核实习》之练习一
udev详解
什么叫微内核,宏内核?
Linux 信号signal处理机制
开发简单的 Linux2.6 内核模块
删除内核的perl脚本
Linux2.6内核usb gadget驱动移植
GCC hacks in the Linux kernel
iomem
kernel学习的想法
让自己的驱动支持udev
linux内核编译步骤
内核的等待队列
Linux内核wait_queue深入分析
升级和删除内核
SD卡驱动分析2
Linux Kernel VDSO本地权限提升漏洞
内核中的TCP的追踪分析-15-TCP(IPV4)的客户端与
linux 2.6内核可加载模块的编译
内核模块HelloWorld
在环回接口上发送一个数据报
ARP初始化
1分钟编译FreeBSD内核
linux设备模型之uart驱动架构分析