# find miniroot/ | cpio -c -o > initrd.img
# gzip initrd.img
这样得到的initrd.img大小是可变的,它取决于您的小型根目录miniroot/的总大小,由于首选使用cpio把根目录进行打包,因此这个initramfs又被称为cpio initrd. 在系统启动阶段,bootload除了从磁盘上机制内核镜像bzImage之外,还要加载initrd.img.gz,然后把initrd.img.gz的起始地址传递给内核。能不能把这两个文件合二为一呢?答案是肯定的.
在Linux 2.6的内核中,可以把initrd.img.gz链接到内核文件(ELF格式)的一个特殊的数据段中,这个段的名字为.init.ramfs。其中全局变量__initramfs_start和__initramfs_end分别指向这个数据段的起始地址和结束地址。内核启动时会对.init.ramfs段中的数据进行解压,然后使用它作为临时的根文件系统。别看这个过程复杂,您只需要在make menuconfig中配置以下选项就可以了:
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
(../miniroot/) Initramfs source file(s)
cpio ramfs 在启动中的处理过程:
cpio ramfs采用rootfs作为其载体, rootfs也是一种基于内存的文件系统格式。
首选在内核启动过程,会初始化rootfs文件系统,rootfs和tmpfs都是内存中的文件系统,其类型为ramfs. 然后会把这个rootf挂载到根目录。 其代码如下:
[start_kernel() -> vfs_caches_init() -> mnt_init()]
void __init mnt_init(void) {
......
init_rootfs();
init_mount_tree();
}
init_rootfs()注册rootfs文件系统,代码如下:
static struct file_system_type rootfs_fs_type = {
.name = "rootfs",
.get_sb = rootfs_get_sb,
.kill_sb = kill_litter_super,
};
int __init init_rootfs(void)
{
err = register_filesystem(&rootfs_fs_type);
......
return err;
}
init_mount_tree会把rootfs挂载到/目录,代码如下:
static void __init init_mount_tree(void)
{
struct vfsmount *mnt;
struct mnt_namespace *ns;
mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
......
set_fs_pwd(current->fs, ns->root, ns->root->mnt_root);
set_fs_root(current->fs, ns->root, ns->root->mnt_root);
}
do_kern_mount()会调用前面注册的rootfs文件系统对象的rootfs_get_sb()函数,
[rootfs_get_sb() -> ramfs_fill_super() -> d_alloc_root()]
struct dentry * d_alloc_root(struct inode * root_inode)
{
struct dentry *res = NULL;
if (root_inode) {
static const struct qstr name = { .name = "/", .len = 1 };
res = d_alloc(NULL, &name);
if (res) {
res->d_sb = root_inode->i_sb;
res->d_parent = res;
d_instantiate(res, root_inode);
}
}
return res;
}
从上面的代码中的可以看出,这个rootfs的dentry对象的名字为"/",也就是根目录了。
然后:
populate_rootfs->unpack_to_rootfs->write_buffer填充上面的rootfs目录
[kernel_init() -> populate_rootfs()]
static int __init populate_rootfs(void)
{
/* 如果__initramfs_end - __initramfs_start不为0,就说明这是和内核文件集成在一起的cpio的intrd。*/
char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end - __initramfs_start, 0);
if (err)
panic(err);
#ifdef CONFIG_BLK_DEV_INITRD
/* 如果initrd_start不为0,说明这是由bootloader加载的initrd,
* 那么需要进一步判断是cpio格式的initrd,还是老式块设备的initrd。
*/
if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
int fd;
/* 首先判断是不是cpio格式的initrd,也就是这里说的initramfs。*/
printk(KERN_INFO "checking if image is initramfs...");
/* 这里unpack_to_rootfs()的最后一个参数为1,表示check only,不会执行解压缩。*/
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 1);
if (!err) {
/* 如果是cpio格式的initrd,把它解压到前面挂载的根文件系统上,然后释放initrd占用的内存。*/
printk(" it is\n");
unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
free_initrd();
return 0;
}
/* 如果执行到这里,说明这是旧的块设备格式的initrd。
* 那么首先在前面挂载的根目录上创建一个initrd.image文件,
* 再把initrd_start到initrd_end的内容写入到/initrd.image中,
* 最后释放initrd占用的内存空间(它的副本已经保存到/initrd.image中了。)。
*/
printk("it isn't (%s); looks like an initrd\n", err);
fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
......
return 0;
}
rootfs_initcall(populate_rootfs);
参考:
阅读(2029) | 评论(0) | 转发(0) |