Chinaunix首页 | 论坛 | 博客
  • 博客访问: 661539
  • 博文数量: 156
  • 博客积分: 4833
  • 博客等级: 上校
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-21 19:36
文章分类

全部博文(156)

文章存档

2016年(2)

2013年(1)

2012年(13)

2011年(30)

2010年(46)

2009年(29)

2008年(23)

2007年(12)

分类: LINUX

2010-04-22 09:53:47




# 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) |
给主人留下些什么吧!~~