海王  

http://www.linuxdiyf.com/viewarticle.php?id=86233

 

  以前一直在使用image-initrd的格式,也就是Linux 2.4内核当中所使用的格式,即便在升级到2.6内核以后,也依然如此,虽然2.6内核开始支持新的cpio-initrd格式。最近开始转向cpio-initrd的格式了,这种格式有不少好处,例如不需要事先生成一个block文件并对其进行格式化,也不必担心这个block文件在使用过程中会不够用而不得不重新制作,也不会因为这个block文件被反复改变以后导致压缩性能下降,好处真的是蛮多。当然也有坏处,呵呵,以往image-initrd只需要把一些准备工作做好,挂载真实根文件系统的工作可以由内核来完成(当然也可以骗过内核不用内核挂载),而在cpio-initrd当中,执行cpio-initrd下的init就是内核做的最后一件事情了,挂载真实根文件系统以及执行/sbin/init都要由cpio-initrd下的init来完成,包括根切换,任务更重了。
  好在这都不算什么难事,不过有一个事情却很让人疑惑。在使用image-initrd格式时,initrd.img当中的内容是被展开到/dev/ram0当中的,在挂载真实文件系统时,可以通过pivot_root进行根切换,并将原来/dev/ram0当中的内容挂载到新根下的某个目录上,此时在新的根目录内还可以访问到initrd.img当中的内容,也可以用umount把所占据的资源释放。而使用cpio-initrd格式时,这一点就完全不同了,cpio-initrd的img在展开时并不依赖于设备文件,即与/dev/ram0无关,其内容是直接存放于rootfs当中,在切换到新的根目录下时,无法使用pivot_root。在mount新根文件系统时,原cpio-initrd当中的内容将无法再访问到,于是原cpio-initrd所占用的空间将无法收回。
  如何解决这个问题?其实在Linux内核Documentation/filesystems/ramfs-rootfs-initramfs.txt当中已经有相关的描述,而我们通常使用的由系统自带的(2.6内核)initrd.img当中,是通过nash所解析的switchroot命令来完成的,这个命令不仅仅是完成了根切换的动作,更重要的是在根切换动作前执行了recursiveRemove的动作,如下面一段代码所示:

QUOTE:

chdir(new);
nashHotplugNewRoot(_nash_context);
nashHotplugNotifyExit(_nash_context);
recursiveRemove("/");
if (mount(new, "/", NULL, MS_MOVE, NULL) < 0) {
eprintf("switchroot: mount failed: %m\n");
close(fd);
return 1;
}

  recursiveRemove("/")就是从(原)根目录开始删除所有的文件,但是这个函数在实现时仅对当前文件系统进行递归操作,因此不会“殃及”新挂载的新根(此时挂在原根的某个目录下)。
  由此可见,对cpio-initrd所占用资源的释放就是通过删除来完成的。但是值得注意的是,这个动作我们几乎无法通过脚本使用通常的命令来完成,因为普通的rm、find等命令都依赖于glibc库,在删除的过程当中一旦glibc库被删除,则rm、find都无法再继续了,而通常之后还需要使用chroot等命令来执行新根目录下的/sbin/init,所以若试图通过普通脚本来完成这样的动作,是需要特别的技巧的。

posted on 2010-04-20 12:31  海王  阅读(668)  评论(0编辑  收藏  举报