LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3021|回复: 1

[转贴+原创]分析内核初始化时根内存盘的加载过程

[复制链接]
发表于 2004-7-26 02:02:48 | 显示全部楼层 |阅读模式
原文出处:
http://www.linuxforum.net/forum/ ... 5&o=7&part=
opera
(enthusiast)
04/27/01 10:41
概述
====
1)当内核配置了内存盘时, 内核在初始化时可以将软盘加载到内存盘中作为根盘.
当同时配置了初始化内
存盘(Initail RAM Disk)时, 内核在初始化时可以在安装主盘之前,
通过引导程序所加载的initrd文件建
立一个内存初始化盘, 首先将它安装成根文件系统, 然后执行其根目录下的linuxrc 文件,
可用于在安装
主盘之前加载一些内核模块. 等到linuxrc 程序退出后, 再将主盘安装成根文件系统,
并将内存初始化盘
转移安装到其/initrd目录下.

2)当主盘就是initrd所生成的内存初始化盘时, 不再进行重新安装,
在DOS下用loadlin加载的抢救盘就是
这种工作方式.

3)引导程序所加载的initrd为文件系统的映象文件, 可以是gzip压缩的, 也可以是不压缩的.
能够识别的
文件系统有minix,ext2,romfs三种.

4)当内核的根盘为软盘时,
内核初始化时会测试软盘的指定部位是否存在文件系统或压缩文件映象, 然后
将之加载或解压到内存盘中作为根盘. 这是单张抢救软盘的工作方式.

有关代码
========
; 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;
}
__initcall(partition_setup);

; drivers/block/rd.c:

int rd_doload;                        /* 1 = load RAM disk, 0 = don't load */
int rd_prompt = 1;                /* 1 = prompt for RAM disk, 0 = don't prompt */
int rd_image_start;                /* starting block # of image */
#ifdef CONFIG_BLK_DEV_INITRD
unsigned long initrd_start, initrd_end;
int mount_initrd = 1;                /* zero if initrd should not be mounted */
int initrd_below_start_ok;

void __init rd_load(void)
{
        rd_load_disk(0); 加载到0号内存盘
}
void __init rd_load_secondary(void)
{
        rd_load_disk(1); 加载到1号内存盘
}
static void __init rd_load_disk(int n)
{
#ifdef CONFIG_BLK_DEV_INITRD
        extern kdev_t real_root_dev;
#endif

        if (rd_doload == 0)
                return;

        if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR 如果根盘是不软盘
#ifdef CONFIG_BLK_DEV_INITRD
                && MAJOR(real_root_dev) != FLOPPY_MAJOR
#endif
        )
                return;

        if (rd_prompt) {
#ifdef CONFIG_BLK_DEV_FD
                floppy_eject();
#endif
#ifdef CONFIG_MAC_FLOPPY
                if(MAJOR(ROOT_DEV) == FLOPPY_MAJOR)
                        swim3_fd_eject(MINOR(ROOT_DEV));
                else if(MAJOR(real_root_dev) == FLOPPY_MAJOR)
                        swim3_fd_eject(MINOR(real_root_dev));
#endif
                printk(KERN_NOTICE
                       "VFS: Insert root floppy disk to be loaded into RAM disk and press ENTER\n");
                wait_for_keypress();
        }

        rd_load_image(ROOT_DEV,rd_image_start, n); 将根软盘加载到n号内存盘

}
void __init initrd_load(void)
{
        ; 使用initrd设备盘作为源盘去建立内存根盘
        rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),rd_image_start,0);
}
static void __init rd_load_image(kdev_t device, int offset, int unit)
{
        struct inode *inode, *out_inode;
        struct file infile, outfile;
        struct dentry in_dentry, out_dentry;
        mm_segment_t fs;
        kdev_t ram_device;
        int nblocks, i;
        char *buf;
        unsigned short rotate = 0;
        unsigned short devblocks = 0;
        char rotator[4] = { '|' , '/' , '-' , '\\' };

        ram_device = MKDEV(MAJOR_NR, unit); 建立输出内存盘设备号

        if ((inode = get_empty_inode()) == NULL)
                return;
        memset(&infile, 0, sizeof(infile));
        memset(&in_dentry, 0, sizeof(in_dentry));
        infile.f_mode = 1; /* read only */
        infile.f_dentry = &in_dentry;
        in_dentry.d_inode = inode;
        infile.f_op = &def_blk_fops;
        init_special_inode(inode, S_IFBLK | S_IRUSR, kdev_t_to_nr(device));
               
        if ((out_inode = get_empty_inode()) == NULL)
                goto free_inode;
        memset(&outfile, 0, sizeof(outfile));
        memset(&out_dentry, 0, sizeof(out_dentry));
        outfile.f_mode = 3; /* read/write */
        outfile.f_dentry = &out_dentry;
        out_dentry.d_inode = out_inode;
        outfile.f_op = &def_blk_fops;
        init_special_inode(out_inode, S_IFBLK | S_IRUSR | S_IWUSR, kdev_t_to_nr(ram_device));
               
        if (blkdev_open(inode, &infile) != 0) 打开输入盘文件
                goto free_inode;
        if (blkdev_open(out_inode, &outfile) != 0) 打开输出内存盘文件
                goto free_inodes;

        fs = get_fs();
        set_fs(KERNEL_DS);
       
        nblocks = identify_ramdisk_image(device, &infile, offset); 鉴定输入盘的文件类型
        if (nblocks < 0) 出错
                goto done;

        if (nblocks == 0) { 表示输入盘是gzip文件
#ifdef BUILD_CRAMDISK
                if (crd_load(&infile, &outfile) == 0) 将输入盘文件解压到输出盘文件中去
                        goto successful_load;
#else
                printk(KERN_NOTICE
                       "RAMDISK: Kernel does not support compressed "
                       "RAM disk images\n");
#endif
                goto done;
        }

        /*
         * NOTE NOTE: nblocks suppose that the blocksize is BLOCK_SIZE, so
         * rd_load_image will work only with filesystem BLOCK_SIZE wide!
         * So make sure to use 1k blocksize while generating ext2fs
         * ramdisk-images.
         */
        if (nblocks > (rd_length[unit] >> BLOCK_SIZE_BITS)) {
                ; 如果输入盘的尺寸超过了输出内存盘的允许尺寸
                printk("RAMDISK: image too big! (%d/%ld blocks)\n",
                       nblocks, rd_length[unit] >> BLOCK_SIZE_BITS);
                goto done;
        }
               
        /*
         * OK, time to copy in the data
         */
        buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);
        if (buf == 0) {
                printk(KERN_ERR "RAMDISK: could not allocate buffer\n");
                goto done;
        }

        if (blk_size[MAJOR(device)])
                devblocks = blk_size[MAJOR(device)][MINOR(device)]; 取输入盘的容量

#ifdef CONFIG_BLK_DEV_INITRD
        if (MAJOR(device) == MAJOR_NR && MINOR(device) == INITRD_MINOR)
                devblocks = nblocks; 如果输入是初始化内存盘,则盘的容量为它的实际尺寸
#endif

        if (devblocks == 0) {
                printk(KERN_ERR "RAMDISK: could not determine device size\n");
                goto done;
        }

        printk(KERN_NOTICE "RAMDISK: Loading %d blocks [%d disk%s] into ram disk... ",
                nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");
        for (i=0; i < nblocks; i++) {
                if (i && (i % devblocks == 0)) {
                        printk("done disk #%d.\n", i/devblocks);
                        rotate = 0;
                        invalidate_buffers(device); 使输入盘设备缓冲区无效
                        if (infile.f_op->release)
                                infile.f_op->release(inode, &infile);
                        printk("lease insert disk #%d and press ENTER\n", i/devblocks+1);
                        wait_for_keypress();
                        if (blkdev_open(inode, &infile) != 0)  {
                                printk("Error opening disk.\n");
                                goto done;
                        }
                        infile.f_pos = 0;
                        printk("Loading disk #%d... ", i/devblocks+1);
                }
                infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos);
                outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos);
#if !defined(CONFIG_ARCH_S390)
                if (!(i % 16)) {
                        printk("%c\b", rotator[rotate & 0x3]);
                        rotate++;
                }
#endif
        }
        printk("done.\n");
        kfree(buf);

successful_load:
        invalidate_buffers(device);
        ROOT_DEV = MKDEV(MAJOR_NR, unit); 将根盘设备设置为当前加载的内存盘
        if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0");

done:
        if (infile.f_op->release)
                infile.f_op->release(inode, &infile);
        set_fs(fs);
        return;
free_inodes: /* free inodes on error */
        iput(out_inode);
        blkdev_put(inode->i_bdev, BDEV_FILE);
free_inode:
        iput(inode);
}
int __init
identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)
{
        const int size = 512;
        struct minix_super_block *minixsb;
        struct ext2_super_block *ext2sb;
        struct romfs_super_block *romfsb;
        int nblocks = -1;
        unsigned char *buf;

        buf = kmalloc(size, GFP_KERNEL);
        if (buf == 0)
                return -1;

        minixsb = (struct minix_super_block *) buf;
        ext2sb = (struct ext2_super_block *) buf;
        romfsb = (struct romfs_super_block *) buf;
        memset(buf, 0xe5, size);

        /*
         * Read block 0 to test for gzipped kernel
         */
        if (fp->f_op->llseek)
                fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0);
        fp->f_pos = start_block * BLOCK_SIZE;
       
        fp->f_op->read(fp, buf, size, &fp->f_pos);
        ; 读取offset开始的512字节
        /*
         * If it matches the gzip magic numbers, return -1
         */
        if (buf[0] == 037 && ((buf[1] == 0213) || (buf[1] == 0236))) {
                printk(KERN_NOTICE
                       "RAMDISK: Compressed image found at block %d\n",
                       start_block);
                nblocks = 0;
                goto done;
        }

        /* romfs is at block zero too */
        if (romfsb->word0 == ROMSB_WORD0 &&
            romfsb->word1 == ROMSB_WORD1) {
                printk(KERN_NOTICE
                       "RAMDISK: romfs filesystem found at block %d\n",
                       start_block);
                nblocks = (ntohl(romfsb->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
                goto done;
        }

        /*
         * Read block 1 to test for minix and ext2 superblock
         */
        if (fp->f_op->llseek)
                fp->f_op->llseek(fp, (start_block+1) * BLOCK_SIZE, 0);
        fp->f_pos = (start_block+1) * BLOCK_SIZE;

        fp->f_op->read(fp, buf, size, &fp->f_pos);
               
        /* Try minix */
        if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
            minixsb->s_magic == MINIX_SUPER_MAGIC2) {
                printk(KERN_NOTICE
                       "RAMDISK: Minix filesystem found at block %d\n",
                       start_block);
                nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
                goto done;
        }

        /* Try ext2 */
        if (ext2sb->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) {
                printk(KERN_NOTICE
                       "RAMDISK: ext2 filesystem found at block %d\n",
                       start_block);
                nblocks = le32_to_cpu(ext2sb->s_blocks_count);
                goto done;
        }

        printk(KERN_NOTICE
               "RAMDISK: Couldn't find valid RAM disk image starting at %d.\n",
               start_block);
       
done:
        if (fp->f_op->llseek)
                fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0);
        fp->f_pos = start_block * BLOCK_SIZE;       

        kfree(buf);
        return nblocks;
}

; fs/super.c
void __init mount_root(void)
{
        struct file_system_type * fs_type;
        struct super_block * sb;
        struct vfsmount *vfsmnt;
        struct block_device *bdev = NULL;
        mode_t mode;
        int retval;
        void *handle;
        char path[64];
        int path_start = -1;


#ifdef CONFIG_BLK_DEV_FD
        if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { 当根盘还是软盘,表示没有加载过内存盘
#ifdef CONFIG_BLK_DEV_RAM
                extern int rd_doload;
                extern void rd_load_secondary(void);
#endif
                floppy_eject();
#ifndef CONFIG_BLK_DEV_RAM
                printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n");
#else
                /* rd_doload is 2 for a dual initrd/ramload setup */
                ; 只有当加载了initrd但没有释放到内存盘中(mount_inird=0)才有可能到这一步
                if(rd_doload==2)
                        rd_load_secondary(); 加载另一张软盘到1号内存盘作为根盘
                else
#endif
                {
                        printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
                        wait_for_keypress();
                }
        }
#endif

        devfs_make_root (root_device_name);
        handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
                                    MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
                                    DEVFS_SPECIAL_BLK, 1);
        if (handle)  /*  Sigh: bd*() functions only paper over the cracks  */
        {
            unsigned major, minor;

            devfs_get_maj_min (handle, &major, &minor);
            ROOT_DEV = MKDEV (major, minor);
        }

        /*
         * Probably pure paranoia, but I'm less than happy about delving into
         * devfs crap and checking it right now. Later.
         */
        if (!ROOT_DEV)
                panic("I have no root and I want to scream");

        bdev = bdget(kdev_t_to_nr(ROOT_DEV));
        if (!bdev)
                panic(__FUNCTION__ ": unable to allocate root device");
        bdev->bd_op = devfs_get_ops (handle);
        path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
        mode = FMODE_READ;
        if (!(root_mountflags & MS_RDONLY))
                mode |= FMODE_WRITE;
        retval = blkdev_get(bdev, mode, 0, BDEV_FS);
        if (retval == -EROFS) {
                root_mountflags |= MS_RDONLY;
                retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS);
        }
        if (retval) {
                /*
                 * Allow the user to distinguish between failed open
                 * and bad superblock on root device.
                 */
                printk ("VFS: Cannot open root device \"%s\" or %s\n",
                        root_device_name, kdevname (ROOT_DEV));
                printk ("lease append a correct \"root=\" boot option\n");
                panic("VFS: Unable to mount root fs on %s",
                        kdevname(ROOT_DEV));
        }

        check_disk_change(ROOT_DEV);
        sb = get_super(ROOT_DEV); 取根盘的超级块
        if (sb) {
                fs_type = sb->s_type;
                goto mount_it;
        }

        read_lock(&file_systems_lock);
        for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
                  if (!(fs_type->fs_flags & FS_REQUIRES_DEV))
                          continue; 根文件系统必须依赖于块设备
                if (!try_inc_mod_count(fs_type->owner))
                        continue; 当文件系统模块正在删除过程中
                read_unlock(&file_systems_lock);
                  sb = read_super(ROOT_DEV,bdev,fs_type,root_mountflags,NULL,1);
建立根盘的超级块结构
                if (sb)
                        goto mount_it;
                read_lock(&file_systems_lock);
                put_filesystem(fs_type); 释放对文件系统模块的引用
        }
        read_unlock(&file_systems_lock);
        panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));

mount_it:
        printk ("VFS: Mounted root (%s filesystem)%s.\n",
                fs_type->name,
                (sb->s_flags & MS_RDONLY) ? " readonly" : "");
        if (path_start >= 0) {
                devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
                                  path + 5 + path_start, NULL, NULL);
                memcpy (path + path_start, "/dev/", 5);
                vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start);
        }
        else
                vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root"); 建立根盘的安装结构
        /* FIXME: if something will try to umount us right now... */
        if (vfsmnt) {
                set_fs_root(current->fs, vfsmnt, sb->s_root); 设置当前进程的根盘和根目录
                set_fs_pwd(current->fs, vfsmnt, sb->s_root);  设置当前进程的当前盘和当前目录
                if (bdev)
                        bdput(bdev); /* sb holds a reference */
                return;
        }
        panic("VFS: add_vfsmnt failed for root fs");
}

#ifdef CONFIG_BLK_DEV_INITRD
int __init change_root(kdev_t new_root_dev,const char *put_old)
{        以new_root_dev作为根盘重新安装根文件系统,原来的根转移到put_old目录下
        struct vfsmount *old_rootmnt;
        struct nameidata devfs_nd, nd;
        int error = 0;

        read_lock(&current->fs->lock);
        old_rootmnt = mntget(current->fs->rootmnt); 取当前进程的根盘安装结构
        read_unlock(&current->fs->lock);
        /*  First unmount devfs if mounted  */
        if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd))
                error = path_walk("/dev", &devfs_nd);
        if (!error) {
                if (devfs_nd.mnt->mnt_sb->s_magic == DEVFS_SUPER_MAGIC &&
                    devfs_nd.dentry == devfs_nd.mnt->mnt_root) {
                        dput(devfs_nd.dentry);
                        down(&mount_sem);
                        /* puts devfs_nd.mnt */
                        do_umount(devfs_nd.mnt, 0, 0);
                        up(&mount_sem);
                } else
                        path_release(&devfs_nd);
        }
        ROOT_DEV = new_root_dev;
        mount_root(); 改变根盘设备重新安装根文件系统
#if 1
        shrink_dcache(); 清除目录项缓冲中所有自由的目录项
        printk("change_root: old root has d_count=%d\n",
               atomic_read(&old_rootmnt->mnt_root->d_count));
#endif
        mount_devfs_fs ();
        /*
         * Get the new mount directory
         */
        error = 0;
        if (path_init(put_old, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd))
                error = path_walk(put_old, &nd); 在新的根盘中寻找put_old目录
        if (error) {
                int blivet;

                printk(KERN_NOTICE "Trying to unmount old root ... ");
                blivet = do_umount(old_rootmnt, 1, 0); 卸载原始的根盘
                if (!blivet) {
                        printk("okay\n");
                        return 0;
                }
                printk(KERN_ERR "error %d\n", blivet);
                return error;
        }
        /* FIXME: we should hold i_zombie on nd.dentry */
        move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old");
        mntput(old_rootmnt);
        path_release(&nd);
        return 0;
}

#endif

static struct vfsmount *add_vfsmnt(struct nameidata *nd, 在虚拟文件系统中的安装点
                                struct dentry *root, 安装盘的根目录项
                                const char *dev_name) 安装盘名称
{       
        struct vfsmount *mnt;
        struct super_block *sb = root->d_inode->i_sb;
        char *name;

        mnt = kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
        if (!mnt)
                goto out;
        memset(mnt, 0, sizeof(struct vfsmount));

        if (nd || dev_name)
                mnt->mnt_flags = MNT_VISIBLE;

        /* It may be NULL, but who cares? */
        if (dev_name) { 复制所安装安装盘名称字符串
                name = kmalloc(strlen(dev_name)+1, GFP_KERNEL);
                if (name) {
                        strcpy(name, dev_name);
                        mnt->mnt_devname = name;
                }
        }
        mnt->mnt_owner = current->uid; 安装盘的安装者
        atomic_set(&mnt->mnt_count,1);
        mnt->mnt_sb = sb; 指向安装盘超级块结构

        spin_lock(&dcache_lock);
        if (nd && !IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
                goto fail;
        mnt->mnt_root = dget(root); 安装盘的根目录项
        mnt->mnt_mountpoint = nd ? dget(nd->dentry) : dget(root); 指向上一层安装盘的根目录项
        mnt->mnt_parent = nd ? mntget(nd->mnt) : mnt; 指向上一层安装盘的安装结构

        if (nd) {
                list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts); 将安装盘作为上一层安装盘的子盘
                list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt); 使安装点目录项指向安装盘
        } else {
                INIT_LIST_HEAD(&mnt->mnt_child); 如果安装盘是根盘
                INIT_LIST_HEAD(&mnt->mnt_clash);
        }
        INIT_LIST_HEAD(&mnt->mnt_mounts);
        list_add(&mnt->mnt_instances, &sb->s_mounts); 该安装结构是安装盘的一个安装实例
        list_add(&mnt->mnt_list, vfsmntlist.prev); 与所有的安装结构相连接
        spin_unlock(&dcache_lock);
out:
        return mnt;
fail:
        spin_unlock(&dcache_lock);
        if (mnt->mnt_devname)
                kfree(mnt->mnt_devname);
        kfree(mnt);
        return NULL;
}
static void move_vfsmnt(struct vfsmount *mnt, 源盘
                        struct dentry *mountpoint, 目标目录
                        struct vfsmount *parent, 目标盘
                        const char *dev_name) 源盘名称
{        将mnt安装盘以dev_name为名称移到parent盘的mountpoint目录下
        struct dentry *old_mountpoint;
        struct vfsmount *old_parent;
        char *new_devname = NULL;

        if (dev_name) {
                new_devname = kmalloc(strlen(dev_name)+1, GFP_KERNEL);
                if (new_devname)
                        strcpy(new_devname, dev_name);
        }

        spin_lock(&dcache_lock);
        old_mountpoint = mnt->mnt_mountpoint; 取源盘的父盘根目录
        old_parent = mnt->mnt_parent; 取源盘的父盘

        /* flip names */
        if (new_devname) {
                if (mnt->mnt_devname)
                        kfree(mnt->mnt_devname); 释放源盘的名称
                mnt->mnt_devname = new_devname; 改为新的名称
        }

        /* flip the linkage */
        mnt->mnt_mountpoint = dget(mountpoint); 指向父盘的根目录
        mnt->mnt_parent = parent ? mntget(parent) : mnt; 指向父盘
        list_del(&mnt->mnt_clash); 删除源盘的安装点对源盘的链接
        list_del(&mnt->mnt_child); 删除源盘的作为子盘的链接
        if (parent) {
                list_add(&mnt->mnt_child, &parent->mnt_mounts); 将源盘作为目标盘的子盘
                list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); 使目标目录指向源盘
        } else {
                INIT_LIST_HEAD(&mnt->mnt_child);
                INIT_LIST_HEAD(&mnt->mnt_clash);
        }
        spin_unlock(&dcache_lock);

        /* put the old stuff */
        dput(old_mountpoint);
        if (old_parent != mnt)
                mntput(old_parent);
}
发表于 2004-7-30 14:43:37 | 显示全部楼层
该文章的initrd涉及的内核版本太旧,而且注释不大有用,缺少图示。
我写了一篇2.6内核的initrd原理详细分析,可以参阅:
http://www.linuxsir.cn/forum.php?mod=viewthread&tid=113586
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表