好在这都不算什么难事,不过有一个事情却很让人疑惑。在使用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的动作,如下面一段代码所示:
引用: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,所以若试图通过普通脚本来完成这样的动作,是需要特别的技巧的。