精华内容
下载资源
问答
  • 简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统。在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系统,也即真实的...
  • Linux 根文件系统挂载过程

    千次阅读 2019-01-04 16:45:46
    Table of Contents ...3.文件系统挂载vfsmount 4.超级块(struct super_bloc) 5.目录索引节点(struct inode): 5.目录项对象(struct dentry): 6.file 结构体 7. 打开的文件集 8.文件查找相关的数据结构...

    Table of Contents

     

    一、代码流程

    二、相关数据结构

    0.基本概念

    1.进程控制块PCB

    2.文件系统类型

    3.文件系统挂载vfsmount

    4.超级块(struct super_bloc)

    5.目录索引节点(struct inode):

    5.目录项对象(struct dentry):

    6.file 结构体

    7. 打开的文件集

    8.文件查找相关的数据结构

    三、注册/创建、安装/挂载rootfs

    1.向内核注册rootfs虚拟文件系统init_rootfs

    2.建立rootfs的根目录,并将rootfs挂载到自己的根目录


    一、代码流程

    在执行kernel_init之前,会建立roofs文件系统。(kernel_init启动用户空间的第一个进程init)

    根文件系统的挂载会经过如下步骤:

    1. [1*]处设置了根目录的名字为“/”。
    2. [2*]处设置了vfsmount中的root目录
    3. [3*]处设置了vfsmount中的超级块
    4. [4*]处设置了vfsmount中的文件挂载点,指向了自己
    5. [5*]处设置了vfsmount中的父文件系统的vfsmount为自己
    start_kernel
      vfs_caches_init
        mnt_init
          init_rootfs注册rootfs文件系统
          init_mount_tree 挂载rootfs文件系统
            vfs_kern_mount
              mount_fs
                type->mount其实是rootfs_mount 
                  mount_nodev
                    fill_super 其实是ramfs_fill_super
                      inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
                      sb->s_root = d_make_root(inode);
                        static const struct qstr name = QSTR_INIT("/", 1);[1*]
                        __d_alloc(root_inode->i_sb, &name);
              ...
              mnt->mnt.mnt_root = root;[2*]
              mnt->mnt.mnt_sb = root->d_sb;[3*]
              mnt->mnt_mountpoint = mnt->mnt.mnt_root;[4*]
              mnt->mnt_parent = mnt;[5*]
                     root.mnt = mnt;
            root.dentry = mnt->mnt_root;
            mnt->mnt_flags |= MNT_LOCKED;
            set_fs_pwd(current->fs, &root);
            set_fs_root(current->fs, &root);
      ...
      rest_init
        kernel_thread(kernel_init, NULL, CLONE_FS);

    二、相关数据结构

    0.基本概念

    从本质上讲,文件系统是特殊的数据分层存储结构,它包含文件、目录和相关的控制信息。为了描述 这个结构,Linux引入了一些基本概念:

    文件 一组在逻辑上具有完整意义的信息项的系列。在Linux中,除了普通文件,其他诸如目录、设备、套接字等 也以文件被对待。总之,“一切皆文件”。

    目录 目录好比一个文件夹,用来容纳相关文件。因为目录可以包含子目录,所以目录是可以层层嵌套,形成 文件路径。在Linux中,目录也是以一种特殊文件被对待的,所以用于文件的操作同样也可以用在目录上。

    目录项 在一个文件路径中,路径中的每一部分都被称为目录项;如路径/home/source/helloworld.c中,目录 /, home, source和文件 helloworld.c都是一个目录项。

    索引节点 用于存储文件的元数据的一个数据结构。文件的元数据,也就是文件的相关信息,和文件本身是两个不同 的概念。它包含的是诸如文件的大小、拥有者、创建时间、磁盘位置等和文件相关的信息。

    超级块 用于存储文件系统的控制信息的数据结构。描述文件系统的状态、文件系统类型、大小、区块数、索引节 点数等,存放于磁盘的特定扇区中。

    如上的几个概念在磁盘中的位置关系如图所示。

    1.进程控制块PCB

    struct task_struct {  
          ......  
          struct thread_info *thread_info;  
          struct list_head tasks;  
          pid_t pid;  
          pid_t tgid;  
          uid_t uid,euid,suid,fsuid;  
          gid_t gid,egid,sgid,fsgid;  
          struct fs_struct *fs;  //本节将大量使用这个  
          struct files_struct *files;  
          ......  
    }  
    

     

    struct fs_struct {
    	int users;
    	spinlock_t lock;
    	seqcount_t seq;
    	int umask;
    	int in_exec;
    	struct path root, pwd;
    };

    2.文件系统类型

    kernel/include/linux/fs.h

    struct file_system_type {
    	const char *name;
    	int fs_flags;
    #define FS_REQUIRES_DEV		1 
    #define FS_BINARY_MOUNTDATA	2
    #define FS_HAS_SUBTYPE		4
    #define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
    #define FS_USERNS_DEV_MOUNT	16 /* A userns mount does not imply MNT_NODEV */
    #define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
    	struct dentry *(*mount) (struct file_system_type *, int,
    		       const char *, void *);  //安装/挂载文件系统时,会调用;获取超级块。
    	void (*kill_sb) (struct super_block *);   //卸载文件系统时会调用。
    	struct module *owner;
    	struct file_system_type * next;      //指向下一个文件系统类型。
    	struct hlist_head fs_supers;      //同一个文件系统类型中所有超级块组成双向链表。 
    
    	struct lock_class_key s_lock_key;
    	struct lock_class_key s_umount_key;
    	struct lock_class_key s_vfs_rename_key;
    	struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];
    
    	struct lock_class_key i_lock_key;
    	struct lock_class_key i_mutex_key;
    	struct lock_class_key i_mutex_dir_key;
    };

    3.文件系统挂载vfsmount

    本质上,mount操作的过程就是新建一个vfsmount结构,然后将此结构和挂载点(目录项对象)关联。关联之后,目录查找时就能沿着vfsmount挂载点一级级向下查找文件了。对于每一个mount的文件系统,都由一个vfsmount实例来表示。

    kernel/include/linux/mount.h

    
        struct vfsmount {  
          struct list_head mnt_hash; //内核通过哈希表对vfsmount进行管理  
          struct vfsmount *mnt_parent;  //指向父文件系统对应的vfsmount  
          struct dentry *mnt_mountpoint; //指向该文件系统挂载点对应的目录项对象dentry  
          struct dentry *mnt_root; //该文件系统对应的设备根目录dentry  
          struct super_block *mnt_sb; //指向该文件系统对应的超级块  
          struct list_head mnt_mounts;   
          struct list_head mnt_child;  //同一个父文件系统中的所有子文件系统通过该字段链接成双联表  
          int mnt_flags;  
          /* 4 bytes hole on 64bits arches */  
          const char *mnt_devname;  /* Name of device e.g. /dev/dsk/hda1 */  
          struct list_head mnt_list;  //所有已挂载文件系统的vfsmount结构通过该字段链接在一起  
          struct list_head mnt_expire;  /* link in fs-specific expiry list */  
          struct list_head mnt_share;   /* circular list of shared mounts */  
          struct list_head mnt_slave_list;/* list of slave mounts */  
          struct list_head mnt_slave;   /* slave list entry */  
          struct vfsmount *mnt_master;  /* slave is on master->mnt_slave_list */  
          struct mnt_namespace *mnt_ns; /* containing namespace */  
          int mnt_id;           /* mount identifier */  
          int mnt_group_id;     /* peer group identifier */  
          /* 
          * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount 
          * to let these frequently modified fields in a separate cache line 
          * (so that reads of mnt_flags wont ping-pong on SMP machines) 
          */  
          atomic_t mnt_count;  
          int mnt_expiry_mark;      /* true if marked for expiry */  
          int mnt_pinned;  
          int mnt_ghosts;  
          /* 
          * This value is not stable unless all of the mnt_writers[] spinlocks 
          * are held, and all mnt_writer[]s on this mount have 0 as their ->count 
          */  
          atomic_t __mnt_writers;  
        };  
    

    4.超级块(struct super_bloc)

    kernel/include/linux/fs.h

    struct super_block {
    	struct list_head	s_list;		/* 指向超级块链表的指针 */
    	dev_t			s_dev;		/* search index; _not_ kdev_t */
    	unsigned char		s_blocksize_bits;
    	unsigned long		s_blocksize;
    	loff_t			s_maxbytes;	/* Max file size */
    	struct file_system_type	*s_type;       //文件系统类型
    	const struct super_operations	*s_op;
    	const struct dquot_operations	*dq_op;
    	const struct quotactl_ops	*s_qcop;
    	const struct export_operations *s_export_op;
    	unsigned long		s_flags;
    	unsigned long		s_magic;
    	struct dentry		*s_root;
    	struct rw_semaphore	s_umount;
    	int			s_count;
    	atomic_t		s_active;
    #ifdef CONFIG_SECURITY
    	void                    *s_security;
    #endif
    	const struct xattr_handler **s_xattr;
    
    	struct list_head	s_inodes;	/* all inodes */
    	struct hlist_bl_head	s_anon;		/* anonymous dentries for (nfs) exporting */
    	struct list_head	s_mounts;	/* list of mounts; _not_ for fs use */
    	struct block_device	*s_bdev;
    	struct backing_dev_info *s_bdi;
    	struct mtd_info		*s_mtd;
    	struct hlist_node	s_instances;
    	struct quota_info	s_dquot;	/* Diskquota specific options */
    
    	struct sb_writers	s_writers;
    
    	char s_id[32];				/* Informational name */
    	u8 s_uuid[16];				/* UUID */
    
    	void 			*s_fs_info;	/* Filesystem private info */
    	unsigned int		s_max_links;
    	fmode_t			s_mode;
    
    	/* Granularity of c/m/atime in ns.
    	   Cannot be worse than a second */
    	u32		   s_time_gran;
    
    	/*
    	 * The next field is for VFS *only*. No filesystems have any business
    	 * even looking at it. You had been warned.
    	 */
    	struct mutex s_vfs_rename_mutex;	/* Kludge */
    
    	/*
    	 * Filesystem subtype.  If non-empty the filesystem type field
    	 * in /proc/mounts will be "type.subtype"
    	 */
    	char *s_subtype;
    
    	/*
    	 * Saved mount options for lazy filesystems using
    	 * generic_show_options()
    	 */
    	char __rcu *s_options;
    	const struct dentry_operations *s_d_op; /* default d_op for dentries */
    
    	/*
    	 * Saved pool identifier for cleancache (-1 means none)
    	 */
    	int cleancache_poolid;
    
    	struct shrinker s_shrink;	/* per-sb shrinker handle */
    
    	/* Number of inodes with nlink == 0 but still referenced */
    	atomic_long_t s_remove_count;
    
    	/* Being remounted read-only */
    	int s_readonly_remount;
    
    	/* AIO completions deferred from interrupt context */
    	struct workqueue_struct *s_dio_done_wq;
    	struct hlist_head s_pins;
    
    	/*
    	 * Keep the lru lists last in the structure so they always sit on their
    	 * own individual cachelines.
    	 */
    	struct list_lru		s_dentry_lru ____cacheline_aligned_in_smp;
    	struct list_lru		s_inode_lru ____cacheline_aligned_in_smp;
    	struct rcu_head		rcu;
    
    	/*
    	 * Indicates how deep in a filesystem stack this SB is
    	 */
    	int s_stack_depth;
    };

    5.目录索引节点(struct inode):

    文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。

    kernel/include/linux/fs.h

    
        struct inode {  
          struct hlist_node i_hash; //哈希表节点  
          struct list_head  i_list;  
          struct list_head  i_sb_list;  
          struct list_head  i_dentry;  
          unsigned long     i_ino;  
          atomic_t      i_count;  
          unsigned int      i_nlink;  
          uid_t         i_uid;     //文件所有者
          gid_t         i_gid;      //文件所有者所属的用户组
          dev_t         i_rdev;  
          u64           i_version;  
          loff_t            i_size;  
        #ifdef __NEED_I_SIZE_ORDERED  
          seqcount_t        i_size_seqcount;  
        #endif  
          struct timespec       i_atime;  
          struct timespec       i_mtime;  //修改时间
          struct timespec       i_ctime;  //创建时间
          unsigned int      i_blkbits;  
          blkcnt_t      i_blocks;  
          unsigned short          i_bytes;  
          umode_t           i_mode;         //文件的读写权限
          spinlock_t        i_lock; /* i_blocks, i_bytes, maybe i_size */  
          struct mutex      i_mutex;  
          struct rw_semaphore   i_alloc_sem;  
          const struct inode_operations *i_op;  
          const struct file_operations  *i_fop; /* former ->i_op->default_file_ops */  
          struct super_block    *i_sb;  
          struct file_lock  *i_flock;  
          struct address_space  *i_mapping;  
          struct address_space  i_data;  
        #ifdef CONFIG_QUOTA  
          struct dquot      *i_dquot[MAXQUOTAS];  
        #endif  
          struct list_head  i_devices;  
          union {  
            struct pipe_inode_info  *i_pipe;  
            struct block_device *i_bdev;  
            struct cdev     *i_cdev;  
          };  
          int           i_cindex;  
          
          __u32         i_generation;  
          
        #ifdef CONFIG_DNOTIFY  
          unsigned long     i_dnotify_mask; /* Directory notify events */  
          struct dnotify_struct *i_dnotify; /* for directory notifications */  
        #endif  
          
        #ifdef CONFIG_INOTIFY  
          struct list_head  inotify_watches; /* watches on this inode */  
          struct mutex      inotify_mutex;  /* protects the watches list */  
        #endif  
          
          unsigned long     i_state;  
          unsigned long     dirtied_when;   /* jiffies of first dirtying */  
          
          unsigned int      i_flags;  
          
          atomic_t      i_writecount;  
        #ifdef CONFIG_SECURITY  
          void          *i_security;  
        #endif  
          void          *i_private; /* fs or device private pointer */  
        };  
    

    5.目录项对象(struct dentry):

    dentry的中文名称是目录项,是Linux文件系统中某个索引节点(inode)的链接。这个索引节点可以是文件,也可以是目录。

    inode(可理解为ext2 inode)对应于物理磁盘上的具体对象,dentry是一个内存实体,其中的d_inode成员指向对应的inode。也就是说,一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量。

    kernel/include/linux/dcache.h

    
        struct dentry {  
          atomic_t d_count;     //一个inode可以在运行的时候链接多个dentry,而d_count记录了这个链接的数量
          unsigned int d_flags;     /* protected by d_lock */  
          spinlock_t d_lock;        /* per dentry lock */  
          int d_mounted;  
          struct inode *d_inode; //目录项对象与目录索引的关联          
          /* Where the name belongs to - NULL is 
          * negative */  
          /* 
          * The next three fields are touched by __d_lookup.  Place them here 
          * so they all fit in a cache line. 
          */  
          struct hlist_node d_hash; //哈希表节点 /* lookup hash list */  
          struct dentry *d_parent; //目录项对象的父亲   /* parent directory */  
          struct qstr d_name; //d_name.name这个是文件名,目录对象与目录名的关联  
          
          struct list_head d_lru;       /* LRU list */  
          /* 
          * d_child and d_rcu can share memory 
          */  
          union {  
            struct list_head d_child;   /* child of parent list */  
            struct rcu_head d_rcu;  
          } d_u;  
          struct list_head d_subdirs;   /* our children */  
          struct list_head d_alias; /* inode alias list */  
          unsigned long d_time;     /* used by d_revalidate */  
          struct dentry_operations *d_op;  
          struct super_block *d_sb; //指向文件系统的超级块/* The root of the dentry tree */  
          void *d_fsdata;           /* fs-specific data */  
          
          unsigned char d_iname[DNAME_INLINE_LEN_MIN];  /* small names */  
        };  
    

    6.file 结构体

    struct file(file结构体):
      struct file结构体定义在include/linux/fs.h中定义。文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file。

    
        struct file {  
          /* 
          * fu_list becomes invalid after file_free is called and queued via 
          * fu_rcuhead for RCU freeing 
          */  
          union {  
            struct list_head    fu_list;  
            struct rcu_head     fu_rcuhead;  
          } f_u;  
          struct path       f_path;  //重要!!!记录挂载信息和目录项信息  
        #define f_dentry    f_path.dentry  
        #define f_vfsmnt    f_path.mnt  
          const struct file_operations  *f_op;  
          atomic_long_t     f_count;  
          unsigned int      f_flags;  
          fmode_t           f_mode;  
          loff_t            f_pos;  
          struct fown_struct    f_owner;  
          const struct cred *f_cred;  
          struct file_ra_state  f_ra;  
          
          u64           f_version;  
        #ifdef CONFIG_SECURITY  
          void          *f_security;  
        #endif  
          /* needed for tty driver, and maybe others */  
          void          *private_data;  
          
        #ifdef CONFIG_EPOLL  
          /* Used by fs/eventpoll.c to link all the hooks to this file */  
          struct list_head  f_ep_links;  
          spinlock_t        f_ep_lock;  
        #endif /* #ifdef CONFIG_EPOLL */  
          struct address_space  *f_mapping;  
        #ifdef CONFIG_DEBUG_WRITECOUNT  
          unsigned long f_mnt_write_state;  
        #endif  
        };  
    

    每个进程在PCB(Process Control Block)即进程控制块中都保存着一份文件描述符表,文件描述符就是这个表的索引,文件描述表中每个表项都有一个指

    向已打开文件的指针,现在我们明确一下:已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体。

    我们在进程中打开一个文件F,实际上就是要在内存中建立F的dentry,和inode结构,并让它们与进程结构联系来,把VFS中定义的接口给接起来。我们来看一看这个经典的图:

    file结构体详解

    7. 打开的文件集

    struct files_struct {//打开的文件集
             atomic_t count;              /*结构的使用计数*/
            ……
             int max_fds;                 /*文件对象数的上限*/
            int max_fdset;               /*文件描述符的上限*/
            int next_fd;                 /*下一个文件描述符*/
            struct file ** fd;           /*全部文件对象数组*/
            ……
     };
    
    struct fs_struct {//建立进程与文件系统的关系
             atomic_t count;              /*结构的使用计数*/
            rwlock_t lock;               /*保护该结构体的锁*/
            int umask;                  /*默认的文件访问权限*/
            struct dentry * root;        /*根目录的目录项对象*/
            struct dentry * pwd;         /*当前工作目录的目录项对象*/
            struct dentry * altroot;    /*可供选择的根目录的目录项对象*/
            struct vfsmount * rootmnt;   /*根目录的安装点对象*/
            struct vfsmount * pwdmnt;    /*pwd的安装点对象*/
            struct vfsmount * altrootmnt;/*可供选择的根目录的安装点对象*/
    };

    回顾上节的图片很容易理解这两个数据结构

    8.文件查找相关的数据结构

    include/linux/fs_struct.h

    
        struct fs_struct {  
          atomic_t count;  
          rwlock_t lock;  
          int umask;  
          struct path root, pwd; //重要!!!记录挂载信息和目录项信息  
        };  
    

    include/linux/namei.h

    
        struct nameidata {  
          struct path   path;  //重要!!!记录挂载信息和目录项信息  
          struct qstr   last;  //重要!!!记录目录名  
          unsigned int  flags;  
          int       last_type;  
          unsigned  depth;  
          char *saved_names[MAX_NESTED_LINKS + 1];  
          
          /* Intent data */  
          union {  
            struct open_intent open;  
          } intent;  
        };  
    

    include/linux/path.h

    
        struct path {  
          struct vfsmount *mnt; //重要!!!记录文件系统挂载信息  
          struct dentry *dentry;  //重要!!!记录目录项信息  
        };  
    
    

    include/linux/dcache.h

     struct qstr {  
      unsigned int hash;  
      unsigned int len;  
      const unsigned char *name;//重要!!!目录/文件名字,如"/","tank1"等具体的文件名  
    }; 

    三、注册/创建、安装/挂载rootfs

    第一步:建立rootfs文件系统;

    第二步:调用其get_sb函数(对于rootfs这种内存/伪文件系统是get_sb_nodev,实际文件系统比如ext2等是get_sb_bdev)、建立超级块(包含目录项和i节点);

    第三步:挂载该文件系统(该文件系统的挂载点指向该文件系统超级块的根目录项);

    第四步:将系统current的根文件系统和根目录设置为rootfs和其根目录。

    kernel/init/main.c

    
        asmlinkage void __init start_kernel(void)  
        {  
          setup_arch(&command_line);//解析uboot命令行,实际文件系统挂载需要  
          parse_args("Booting kernel", static_command_line, __start___param,  
                   __stop___param - __start___param,  
                   &unknown_bootoption);  
          vfs_caches_init(num_physpages);  
        }  
    
    
    

    kernel/fs/dcache.c

    
        void __init vfs_caches_init(unsigned long mempages)  
        {  
          mnt_init();  
          bdev_cache_init(); //块设备文件创建  
          chrdev_init();//字符设备文件创建  
        }  
    

    kernel/fs/namespace.c

     void __init mnt_init(void)  
    {  
      init_rootfs(); //向内核注册rootfs  
      init_mount_tree();//重要!!!rootfs根目录的建立以及rootfs文件系统的挂载;设置系统current根目录和根文件系统为rootfs  
    } 

    1.向内核注册rootfs虚拟文件系统init_rootfs

    kernel/fs/ramfs/inode.c

    
        int __init init_rootfs(void)  
        {  
          err = register_filesystem(&rootfs_fs_type);  
        }  
        static struct file_system_type rootfs_fs_type = {  
          .name     = "rootfs",  
          .get_sb       = rootfs_get_sb,  
          .kill_sb  = kill_litter_super,  
        };  
    

    2.建立rootfs的根目录,并将rootfs挂载到自己的根目录

    kernel/fs/namespace.c


    
        static void __init init_mount_tree(void)  
        {  
          struct vfsmount *mnt;  
          struct mnt_namespace *ns;  
          struct path root;  
          //创建rootfs的vfsmount结构,建立rootfs的超级块、并将rootfs挂载到自己的根目录。  
          /* 
          mnt->mnt_mountpoint = mnt->mnt_root = dget(sb->s_root),而该mnt和自己的sb是关联的; 
          所以,是把rootfs文件系统挂载到了自己对应的超级块的根目录上。 
          这里也是实现的关键:一般文件系统的挂载是调用do_mount->do_new_mount而该函数中首先调用do_kern_mount,这时mnt->mnt_mountpoint = mnt->mnt_root;但后边 
          它还会调用do_add_mount->graft_tree->attach_recursive_mnt如下代码mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt)改变了其挂载点!!! 
          */  
          mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);  
          list_add(&mnt->mnt_list, &ns->list);  
          ns->root = mnt; //将创建好的mnt加入系统当前  
          mnt->mnt_ns = ns;  
          
          init_task.nsproxy->mnt_ns = ns; //设置进程的命名空间  
          get_mnt_ns(ns);  
          
          root.mnt = ns->root; //文件系统为rootfs,相当与root.mnt = mnt;  
          root.dentry = ns->root->mnt_root;//目录项为根目录项,相当与root.dentry = mnt->mnt_root;  
          
          //设置系统current的pwd目录和文件系统  
          set_fs_pwd(current->fs, &root);  
          //设置系统current根目录,根文件系统。这个是关键!!!整个内核代码最多只有两处调用  
          set_fs_root(current->fs, &root);    
        }  
    
    

     

    展开全文
  • Linux 根文件系统挂载分析.pdf
  • 解析linux根文件系统挂载过程

    热门讨论 2010-07-16 00:57:07
    根据Linux Kernel代码,详细解析kernel启动过程中,根文件系统挂载过程。
  • 嵌入式Linux根文件系统挂载

    千次阅读 2017-12-21 17:12:47
    嵌入式Linux系统由三部分组成: uboot、kernel、根文件系统, 还是这张老图 这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统 那么什么是根呢?哈哈 其实根表示的就是第一个的意思 ...

    嵌入式Linux系统由三部分组成: uboot、kernel、根文件系统, 还是这张老图


    这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统

    那么什么是根呢?哈哈 其实根表示的就是第一个的意思

    下面贴张图看看整个根文件系统制作的步骤:


    第一步创建目录,这里是在我自己的一个目录下建立一个名为rootfs的目录:


    第二步 :创建设备文件(比如操作串口,其实就是打开一个串口文件,向里面写入读出等)

    这里有两个是必须的,其他的暂时不考虑(串口控制台)(这个在dev目录下执行)


    第一个指令console表示是串口控制台设备 c表示是字符设备 5表示主设备号,1表示次设备号

    第二个指令暂时也不明白,先放着。

    第三步:加入配置文件(主要是etc目录下的)


    第四步:添加内核模块


    这个要进入到Llinux 内核中编译内核模块!


    第二条指令是将编译好的模块拷贝到rootfs目录下的相应目录中


    从这里可以看到当前目录下的一些.ko文件


    第五步:编译安装busybox


    先解压,然后进入目录make menuconfig



    这里直接进入第一项,这里有两项要设置

    1. 选中第一项Build BusyBox as a static binary 

    2. Corss Compile prefix 输入自己交叉编译器的前缀




    上图中需要设置两个地方,一个是选中第一项 一个是BusyBox installation prefix安装路径这里填写我自己建的目录所在的绝对路径。


    上图中的home写错了,这里先说一下。退出然后保存,make 编译然后make install(其实这里就是执行一个复制功能)(上面的选项其他的暂不配置)安装成功后截图:


    这是也可以去rootfs/bin目录下看看有什么变化,执行之前bin目录下是空的


    这里可以看到很多常用的命令!


    第二部分:挂载根文件系统(即如何使用上边制作好的根文件系统)





    第一步:


    Initramfs 启动速度快。缺点是掉电后数据丢失!

    进入rootfs目录下,创建一个软链接!把系统的Initramfs链接到当前的目录下的busybox下 软链接的名字取为init

    然后进入Linux内核中配置make menuconfig配置内核


    进入general setup


    选中图中蓝色部分上面一项Initial RAM filessystem and RAM disk.......

    并且进入图中蓝色部分写入自己执着的rootfs路径:


    保存,然后重新编译内核


    编译完成:


    这里将生成的 arch/arm/boot/uImage文件拷贝到tftpboot目录下!(这个可以参考前面的关于自动启动内核设置那篇博文)


    最后一步,设置环境变量:

    打开串口终端,连接开发板

    设置环境变量:



    然后tftp uImage c0008000

    bootm c0008000启动内核

    这里可以看到上面制作的文件系统启动成功


    下面一步来讲述使用NFS来挂着根文件系统!(network file system)

    这样有利于开发过程中的同步,即在PC机上更改一些文件能同时同步到开发板上的文件上

    这样就省去了每次更改一些内核文件时重新编译工作!(内核开发过程中非常实用,使用NFS是必须掌握的技巧)

    首先第一步还是内核配置:

    首先取消掉上面选中的Initial RAM filesystem and RAM disk...选项,如图:(这一项是在Gnenral setup选项里面)


    然后进入file systems中


    进入选中图中蓝色选项:


    选中图中蓝色选项,并且进入,选中图中蓝色部分


    然后退出保存,然后在重新编译make uImage ARCH=arm CROSS_COMPILE=arm-linux-


    同上将uImage拷贝到tftpboot目录下,

    然后剩下的就是设置nfs环境变量了



    我电脑linux的ip 是192.168.1.111, 开发板的ip是192.168.1.110

    所以这里我设置的环境变量为

    [html]  view plain  copy
    1. <span style="font-size:24px;color:#ff0000;">setenv bootargs noinitrd console=ttySAC0,115200 init=/init root=/dev/nfs rw nfsroot=192.168.1.111:/home/S3-ARM/rootfs,proto=tcp,nfsvers=3 ip=192.168.1.110:192.168.1.111:192.168.1.1:255.255.255.0::eth0:off</span>  
    [html]  view plain  copy
    1.   
    设置完后然后saveenv一下

    tftp c0008000 uImage

    bootm c0008000

    然后看一张截图,就是我在pc机的linux上的rootfs目录下建立一个Text.txt文件,可以看到这个文件立刻就可以在开发板上看到。




    这在内核开发基本是必备技能!用起来是相当方便的!

    这里NFS挂载补充一点:/etc/init.d/nfs restart 启动nfs服务器

    第二点:

    vim /etc/exports 文件 输入一下内容,这个是我的rootfs文件路径(这里的意思就是NFS要挂在的目录是哪个,然后挂载属性是什么)



    展开全文
  • 嵌入式Linux根文件系统制作和挂载

    万次阅读 多人点赞 2016-05-02 20:33:53
    嵌入式Linux系统由三部分组成: uboot、kernel、根文件系统, 还是这张老图 这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统 那么什么是根呢?哈哈 其实根表示的就是第一个的意思 下面贴...

    嵌入式Linux系统由三部分组成: uboot、kernel、根文件系统, 还是这张老图


    这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统

    那么什么是根呢?哈哈 其实根表示的就是第一个的意思

    下面贴张图看看整个根文件系统制作的步骤:


    第一步创建目录,这里是在我自己的一个目录下建立一个名为rootfs的目录:


    第二步 :创建设备文件(比如操作串口,其实就是打开一个串口文件,向里面写入读出等)

    这里有两个是必须的,其他的暂时不考虑(串口控制台)(这个在dev目录下执行)


    第一个指令console表示是串口控制台设备 c表示是字符设备 5表示主设备号,1表示次设备号

    第二个指令暂时也不明白,先放着。

    第三步:加入配置文件(主要是etc目录下的)


    第四步:添加内核模块


    这个要进入到Llinux 内核中编译内核模块!


    第二条指令是将编译好的模块拷贝到rootfs目录下的相应目录中


    从这里可以看到当前目录下的一些.ko文件


    第五步:编译安装busybox


    先解压,然后进入目录make menuconfig



    这里直接进入第一项,这里有两项要设置

    1. 选中第一项Build BusyBox as a static binary 

    2. Corss Compile prefix 输入自己交叉编译器的前缀




    上图中需要设置两个地方,一个是选中第一项 一个是BusyBox installation prefix安装路径这里填写我自己建的目录所在的绝对路径。


    上图中的home写错了,这里先说一下。退出然后保存,make 编译然后make install(其实这里就是执行一个复制功能)(上面的选项其他的暂不配置)安装成功后截图:


    这是也可以去rootfs/bin目录下看看有什么变化,执行之前bin目录下是空的


    这里可以看到很多常用的命令!


    第二部分:挂载根文件系统(即如何使用上边制作好的根文件系统)





    第一步:


    Initramfs 启动速度快。缺点是掉电后数据丢失!

    进入rootfs目录下,创建一个软链接!把系统的Initramfs链接到当前的目录下的busybox下 软链接的名字取为init

    然后进入Linux内核中配置make menuconfig配置内核


    进入general setup


    选中图中蓝色部分上面一项Initial RAM filessystem and RAM disk.......

    并且进入图中蓝色部分写入自己执着的rootfs路径:


    保存,然后重新编译内核


    编译完成:


    这里将生成的 arch/arm/boot/uImage文件拷贝到tftpboot目录下!(这个可以参考前面的关于自动启动内核设置那篇博文)


    最后一步,设置环境变量:

    打开串口终端,连接开发板

    设置环境变量:



    然后tftp uImage c0008000

    bootm c0008000启动内核

    这里可以看到上面制作的文件系统启动成功


    下面一步来讲述使用NFS来挂着根文件系统!(network file system)

    这样有利于开发过程中的同步,即在PC机上更改一些文件能同时同步到开发板上的文件上

    这样就省去了每次更改一些内核文件时重新编译工作!(内核开发过程中非常实用,使用NFS是必须掌握的技巧)

    首先第一步还是内核配置:

    首先取消掉上面选中的Initial RAM filesystem and RAM disk...选项,如图:(这一项是在Gnenral setup选项里面)


    然后进入file systems中


    进入选中图中蓝色选项:


    选中图中蓝色选项,并且进入,选中图中蓝色部分


    然后退出保存,然后在重新编译make uImage ARCH=arm CROSS_COMPILE=arm-linux-


    同上将uImage拷贝到tftpboot目录下,

    然后剩下的就是设置nfs环境变量了



    我电脑linux的ip 是192.168.1.111, 开发板的ip是192.168.1.110

    所以这里我设置的环境变量为

    <span style="font-size:24px;color:#ff0000;">setenv bootargs noinitrd console=ttySAC0,115200 init=/init root=/dev/nfs rw nfsroot=192.168.1.111:/home/S3-ARM/rootfs,proto=tcp,nfsvers=3 ip=192.168.1.110:192.168.1.111:192.168.1.1:255.255.255.0::eth0:off</span>
    
    
    设置完后然后saveenv一下

    tftp c0008000 uImage

    bootm c0008000

    然后看一张截图,就是我在pc机的linux上的rootfs目录下建立一个Text.txt文件,可以看到这个文件立刻就可以在开发板上看到。




    这在内核开发基本是必备技能!用起来是相当方便的!

    这里NFS挂载补充一点:/etc/init.d/nfs restart 启动nfs服务器

    第二点:

    vim /etc/exports 文件 输入一下内容,这个是我的rootfs文件路径(这里的意思就是NFS要挂在的目录是哪个,然后挂载属性是什么)



    展开全文
  • Linux根文件系统挂载过程详细分析

    千次阅读 2014-08-21 14:42:12
    简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统。在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系统,也即真实的...

    简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统。在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_kern_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系统,也即真实的文件系统。

    一.什么是根文件系统

    在传统的Windows机器上目录结构中,可能会包括C:或者D:盘,而他们一般就称之为特定逻辑磁盘的根目录。从文件系统的层面来说,每一个分区都包含了一个根目录区,也即系统中存在多个根目录。

    但是,在Linux系统中,目录结构与Windows上有较大的不同。系统中只有一个根目录,路径是“/”,而其它的分区只是挂载在根目录中的一个文件夹,如“/proc”和“system”等,这里的“/”就是Linux中的根目录。

    对应根目录也就存在一个根目录文件系统的概念,我们可以将某一个分区挂载为根目录文件系统,如6410公版中就将mtdblk2挂载为根目录文件系统。程序中可以通过U-Boot给Kernel指定参数或者编译选项来指定,如目前的开发板中就通过如下的编译选项来制定根目录文件系统:

    CONFIG_CMDLINE="console=ttyS0,115200 mem=108M rdinit=/linuxrc root=/dev/mtdblock2"

    简单的来说,根目录文件系统就是一种目录结构,包括了Linux启动的时候所必须的一些目录结构和重要文件。

    根文件系统有两种,一种是虚拟根文件系统,另外一种是真实的根文件系统。一般情况下,会首先在虚拟的根文件系统中做一部分工作,然后切换到真实的根文件系统下面。

    笼统的来说,虚拟的根文件系统包括三种类型,即Initramfs、cpio-initrd和image-initrd。

    二.相关重要概念

    1. Initrd

    Initrd是在Linux中普遍采用的一种技术,就是由Bootloader加载的内存盘。在系统启动的过程中,首先会执行Initrd中的“某一个文件” 来完成驱动模块加载的任务,第二阶段才会执行真正的根文件系统中的/sbin/init。这里提到的第一阶段是为第二阶段服务的,主要是用来加载根文件系统以及根文件系统存储介质的驱动程序。

    资料中提到,存在多种类型的Initrd,实际应用中包括无Initrd、Linux Kernel和Initrd打包、Linux Kernel和Initrd分离以及RAMDisk Initrd。

    目前,手中项目采用的就是第四种策略。在系统启动的时候,U-Boot会将Linux Kernel和Rootfs加载到内存,并跳转到Linux Kernel的入口地址执行程序。这篇文章将侧重对该种情况进行分析。

    三.根文件系统加载代码分析

    1. VFS的注册

    首先不得不从老掉牙的Linux系统的函数start_kernel()说起。函数start_kernel()中会去调用vfs_caches_init()来初始化VFS。

    下面看一下函数vfs_caches_init ()的代码:

    void __init vfs_caches_init(unsigned long mempages)

    {

    unsigned long reserve;

    /* Base hash sizes on available memory, with a reserve equal to

    150% of current kernel size */

    reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);

    mempages -= reserve;

    names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,

    SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

    dcache_init();

    inode_init();

    files_init(mempages);

    [1] mnt_init();

    bdev_cache_init();

    chrdev_init();

    }

    代码【1】:vfs_caches_init()中最重要的函数。函数mnt_init()会创建一个rootfs,这是个虚拟的rootfs,即内存文件系统,后面还会指向真实的文件系统。

    接下来看一下函数mnt_init():

    Void __init mnt_init(void)

    {

    unsigned u;

    int err;

    init_rwsem(&namespace_sem);

    mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),

    0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

    mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

    if (!mount_hashtable)

    panic("Failed to allocate mount hash table/n");

    printk("Mount-cache hash table entries: %lu/n", HASH_SIZE);

    for (u = 0; u < HASH_SIZE; u++)

    INIT_LIST_HEAD(&mount_hashtable[u]);

    err = sysfs_init();

    if (err)

    printk(KERN_WARNING "%s: sysfs_init error: %d/n",

    __func__, err);

    fs_kobj = kobject_create_and_add("fs", NULL);

    if (!fs_kobj)

    printk(KERN_WARNING "%s: kobj create error/n", __func__);

    [1] init_rootfs();

    [2] init_mount_tree();

    }

    代码[1]:创建虚拟根文件系统;

    代码[2]:注册根文件系统。

    接下来看一下函数init_mount_tree()的代码:

    static void __init init_mount_tree(void)

    {

    struct vfsmount *mnt;

    struct mnt_namespace *ns;

    struct path root;

    [1] mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);

    if (IS_ERR(mnt))

    panic("Can't create rootfs");

    ns = kmalloc(sizeof(*ns), GFP_KERNEL);

    if (!ns)

    panic("Can't allocate initial namespace");

    atomic_set(&ns->count, 1);

    INIT_LIST_HEAD(&ns->list);

    init_waitqueue_head(&ns->poll);

    ns->event = 0;

    list_add(&mnt->mnt_list, &ns->list);

    ns->root = mnt;

    mnt->mnt_ns = ns;

    init_task.nsproxy->mnt_ns = ns;

    get_mnt_ns(ns);

    root.mnt = ns->root;

    root.dentry = ns->root->mnt_root;

    set_fs_pwd(current->fs, &root);

    [2] set_fs_root(current->fs, &root);

    }

    代码[1]:创建虚拟文件系统;

    代码[2]:将当前的文件系统配置为根文件系统。

    可能有人会问,为什么不直接把真实的文件系统配置为根文件系统?

    答案很简单,内核中没有根文件系统的设备驱动,如USB等存放根文件系统的设备驱动,而且即便你将根文件系统的设备驱动编译到内核中,此时它们还尚未加载,其实所有的Driver是由在后面的Kernel_Init线程进行加载。所以需要CPIO Initrd、Initrd和RAMDisk Initrd。另外,我们的Root设备都是以设备文件的方式指定的,如果没有根文件系统,设备文件怎么可能存在呢?

    2. VFS的挂载

    接下来,Kernel_Start会去调用rest_init()并会去创建系统中的第一个进程Kernel_Init,并由其调用所有模块的初始化函数,其中ROOTFS的初始化函数也在这个期间被调用。

    函数rest_init代码如下:

    /*

    * We need to finalize in a non-__init function or else race conditions

    * between the root thread and the init thread may cause start_kernel to

    * be reaped by free_initmem before the root thread has proceeded to

    * cpu_idle.

    *

    * gcc-3.4 accidentally inlines this function, so use noinline.

    */

    static noinline void __init_refok rest_init(void)

    __releases(kernel_lock)

    {

    int pid;

    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

    numa_default_policy();

    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

    kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

    unlock_kernel();

    /*

    * The boot idle thread must execute schedule()

    * at least once to get things moving:

    */

    init_idle_bootup_task(current);

    rcu_scheduler_starting();

    preempt_enable_no_resched();

    schedule();

    preempt_disable();

    /* Call into cpu_idle with preempt disabled */

    cpu_idle();

    }

    函数Kernel_Init代码如下:

    static int __init kernel_init(void * unused)

    {

    lock_kernel();

    /*

    * init can run on any cpu.

    */

    set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);

    /*

    * Tell the world that we're going to be the grim

    * reaper of innocent orphaned children.

    *

    * We don't want people to have to make incorrect

    * assumptions about where in the task array this

    * can be found.

    */

    init_pid_ns.child_reaper = current;

    cad_pid = task_pid(current);

    smp_prepare_cpus(setup_max_cpus);

    do_pre_smp_initcalls();

    start_boot_trace();

    smp_init();

    sched_init_smp();

    cpuset_init_smp();

    [1] do_basic_setup();

    /*

    * check if there is an early userspace init. If yes, let it do all

    * the work

    */

    [2] if (!ramdisk_execute_command)

    ramdisk_execute_command = "/init";

    [3] if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

    ramdisk_execute_command = NULL;

    prepare_namespace();

    }

    /*

    * 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..

    */

    init_post();

    return 0;

    }

    代码[1]:函数do_basic_setup()调用所有模块的初始化函数,包括initramfs的初始化函数populate_rootfs。这部分代码在init/initramfs.c下面,函数populate_rootfs通过如下方式导出:

    rootfs_initcall(populate_rootfs);

    代码[2]:ramdisk_execute_command值通过“rdinit=”指定,如果未指定,则采用默认的值/init。

    代码[3]:检查根文件系统中是否存在文件ramdisk_execute_command,如果存在的话则执行init_post(),否则执行prepare_namespace()挂载根文件系统。

    需要特别指出的是initramfs.c模块的入口函数populate_rootfs()是否执行取决于Kernel的编译选项。参照linux/init目录下的makefile文件,如下:

    #

    # Makefile for the linux kernel.

    #

    obj-y := main.o version.o mounts.o

    ifneq ($(CONFIG_BLK_DEV_INITRD),y)

    obj-y += noinitramfs.o

    else

    obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o

    endif

    obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o

    mounts-y := do_mounts.o

    mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o

    mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o

    mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o

    主要完成Initrd的检测工作,检查出是CPIO Initrd还是Initramfs还是Image-Initrd还是需要在编译的时候做如下的配置(General setupàInitramfs/initrd support):

    clip_image002

    该函数的代码如下:

    static int __init populate_rootfs(void)

    {

    [1] char *err = unpack_to_rootfs(__initramfs_start,

    __initramfs_end - __initramfs_start, 0);

    if (err)

    panic(err);

    [2] if (initrd_start) {

    #ifdef CONFIG_BLK_DEV_RAM

    int fd;

    printk(KERN_INFO "checking if image is initramfs...");

    [3] err = unpack_to_rootfs((char *)initrd_start,

    initrd_end - initrd_start, 1);

    if (!err) {

    printk(" it is/n");

    unpack_to_rootfs((char *)initrd_start,

    initrd_end - initrd_start, 0);

    free_initrd();

    return 0;

    }

    printk("it isn't (%s); looks like an initrd/n", err);

    [4] fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);

    if (fd >= 0) {

    [5] sys_write(fd, (char *)initrd_start,

    initrd_end - initrd_start);

    sys_close(fd);

    [6] free_initrd();

    }

    #else

    printk(KERN_INFO "Unpacking initramfs...");

    [7] err = unpack_to_rootfs((char *)initrd_start,

    initrd_end - initrd_start, 0);

    if (err)

    panic(err);

    printk(" done/n");

    free_initrd();

    #endif

    }

    return 0;

    }

    代码[1]:unpack_to_rootfs顾名思义,就是解压包到rootfs,其具有两个功能,一个是检测是否是属于cpio包,另外一个就是解压cpio包,通过最后一个参数进行控制。1:检测,0:解压。其实,Initramfs也是压缩过后的CPIO文件。

    资料中提到,Linux2.5中开始引入initramfs,在Linux2.6中一定存在,而且编译的时候通过连接脚本arch/arm/kernel/vmlinux.lds将其编译到__initramfs_start~__initramfs_end,执行完unpack_to_rootfs后将被拷贝到根目录。

    代码[2]:判断是否加载了Initrd,无论对于那种格式的Initrd,即无论是CPIO-Initrd还是Image-Initrd,U-Boot都会将其拷贝到initrd_start。当然了,如果是initramfs的情况下,该值肯定为空了。

    代码[3]:判断加载的是不是CPIO-Initrd。

    通过在这里主要用于检测,如果是编译到Linux Kernel的CPIO Initrd,__initramfs_end - __initramfs_start应该是大于零的,否则为零,其实也就是通过这里来判断是否为CPIO Initrd。

    代码[4]:如果不是CPIO-Initrd,则就是Image-Initrd,将其内容保存到文件/initrd.image中。在根文件系统中创建文件/initrd.image。

    代码[5]:这里是对Image-Initrd提供支持的,将内存中的initrd赋值到initrd.image中,以释放内存空间。

    代码[6]:释放Initrd所占用的内存空间。

    另外,如果要支持Image-Initrd的话,必须要配置CONFIG_BLK_DEV_RAM,配置的方法上面已经讲过。

    下面接着来分析函数kernel_init

    static int __init kernel_init(void * unused)

    {

    do_basic_setup();

    /*

    * check if there is an early userspace init. If yes, let it do all

    * the work

    */

    if (!ramdisk_execute_command)

    ramdisk_execute_command = "/init";

    [1] if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

    ramdisk_execute_command = NULL;

    prepare_namespace();

    }

    /*

    * 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..

    */

    init_post();

    return 0;

    }

    代码[1]:前面在对函数populate_rootfs进行分析的时候已经知道,对于initramfs和cpio-initrd的情况,都会将文件系统(其实是一个VFS)解压到根文件系统。如果虚拟文件系统中存在ramdisk_execute_command指定的文件则直接转向init_post()来执行,否则执行函数prepare_namespace()。

    3. 根文件系统的挂载

    从上面的代码分析中知道,对于Image-Initrd或者VFS(即InitRamfs或者CPIO-Initrd)中不存在文件ramdisk_execute_command的情况,则执行prepare_namespace()。

    接下来看一下函数prepare_namespace()的代码:

    /*

    * Prepare the namespace - decide what/where to mount, load ramdisks, etc.

    */

    void __init prepare_namespace(void)

    {

    int is_floppy;

    [1] if (root_delay) {

    printk(KERN_INFO "Waiting %dsec before mounting root device.../n",

    root_delay);

    ssleep(root_delay);

    }

    /*

    * wait for the known devices to complete their probing

    *

    * Note: this is a potential source of long boot delays.

    * For example, it is not atypical to wait 5 seconds here

    * for the touchpad of a laptop to initialize.

    */

    [2] wait_for_device_probe();

    md_run_setup();

    [3] if (saved_root_name[0]) {

    root_device_name = saved_root_name;

    if (!strncmp(root_device_name, "mtd", 3) ||

    !strncmp(root_device_name, "ubi", 3)) {

    [4] mount_block_root(root_device_name, root_mountflags);

    goto out;

    }

    [5] ROOT_DEV = name_to_dev_t(root_device_name);

    if (strncmp(root_device_name, "/dev/", 5) == 0)

    root_device_name += 5;

    }

    [6] if (initrd_load())

    goto out;

    [7] /* wait for any asynchronous scanning to complete */

    if ((ROOT_DEV == 0) && root_wait) {

    printk(KERN_INFO "Waiting for root device %s.../n",

    saved_root_name);

    while (driver_probe_done() != 0 ||

    (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)

    msleep(100);

    async_synchronize_full();

    }

    is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

    if (is_floppy && rd_doload && rd_load_disk(0))

    ROOT_DEV = Root_RAM0;

    mount_root();

    out:

    [9] sys_mount(".", "/", NULL, MS_MOVE, NULL);

    [10] sys_chroot(".");

    }

    代码[1]:资料中提到,对于将根文件系统存放到USB或者SCSI设备上的情况,Kernel需要等待这些耗费时间比较久的设备驱动加载完毕,所以这里存在一个Delay。

    代码[2]:从字面的意思来看,这里也是来等待根文件系统所在的设备探测函数的完成。

    代码[3]:参数saved_root_name存放的是Kernel参数root=所指定的设备文件,这点不再赘述,可以参照代码。

    代码[4]:按照资料中的解释,这里相当于将saved_root_nam指定的设备进行加载。如下面传递给内核的command line:

    CONFIG_CMDLINE="console=ttyS0,115200 mem=108M rdinit=/linuxrc root=/dev/mtdblock2"

    实际上就是加载/dev/mtdblock2。

    代码[5]:参数ROOT_DEV存放设备节点号。

    代码[6]:挂载initrd,这里进行的操作相当的复杂,可以参照后续关于该函数的详细解释。

    代码[7]:如果指定mount_initrd为true,即没有指定在函数initrd_load中mount的话,则在这里重新realfs的mount操作。

    代码[9]:将挂载点从当前目录(实际当前的目录在mount_root中或者在mount_block_root中指定)移到根目录。对于上面的command line的话,当前的目录就是/dev/mtdblock2。

    代码[10]:将当前目录当作系统的根目录,至此虚拟系统根目录文件系统切换到了实际的根目录文件系统。

    接下来看一下函数initrd_load()的代码:

    int __init initrd_load(void)

    {

    [1] if (mount_initrd) {

    [2] create_dev("/dev/ram", Root_RAM0);

    /*

    * Load the initrd data into /dev/ram0. Execute it as initrd

    * unless /dev/ram0 is supposed to be our actual root device,

    * in that case the ram disk is just set up here, and gets

    * mounted in the normal path.

    */

    [3] if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {

    sys_unlink("/initrd.image");

    [4] handle_initrd();

    return 1;

    }

    }

    sys_unlink("/initrd.image");

    return 0;

    }

    代码[1]:可以通过Kernel的参数“noinitrd“来配置mount_initrd的值,默认为1,很少看到有项目区配置该值,所以一般情况下,mount_initrd的值应该为1;

    代码[2]:创建一个Root_RAM0的设备节点/dev/ram;

    代码[3]:如果根文件设备号不是Root_RAM0则程序就会执行代码[4],换句话说,就是给内核指定的参数不是/dev/ram,例如上面指定的/dev/mtdblock2设备节点肯定就不是Root_RAM0。

    另外这行代码还将文件initrd.image释放到节点/dev/ram0,也就是对应image-initrd的操作。

    代码[4]:函数handle_initrd主要功能是执行Initrd中的linuxrc文件,并且将realfs的根目录设置为当前目录。其实前面也已经提到了,这些操作只对image-cpio的情况下才会去执行。

    函数handle_initrd的代码如下:

    static void __init handle_initrd(void)

    {

    int error;

    int pid;

    [1] real_root_dev = new_encode_dev(ROOT_DEV);

    [2] create_dev("/dev/root.old", Root_RAM0);

    /* mount initrd on rootfs' /root */

    mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);

    [3] sys_mkdir("/old", 0700);

    root_fd = sys_open("/", 0, 0);

    old_fd = sys_open("/old", 0, 0);

    /* move initrd over / and chdir/chroot in initrd root */

    [4] sys_chdir("/root");

    sys_mount(".", "/", NULL, MS_MOVE, NULL);

    sys_chroot(".");

    /*

    * In case that a resume from disk is carried out by linuxrc or one of

    * its children, we need to tell the freezer not to wait for us.

    */

    current->flags |= PF_FREEZER_SKIP;

    [5] pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);

    if (pid > 0)

    while (pid != sys_wait4(-1, NULL, 0, NULL))

    yield();

    current->flags &= ~PF_FREEZER_SKIP;

    /* move initrd to rootfs' /old */

    sys_fchdir(old_fd);

    sys_mount("/", ".", NULL, MS_MOVE, NULL);

    /* switch root and cwd back to / of rootfs */

    [6] sys_fchdir(root_fd);

    sys_chroot(".");

    sys_close(old_fd);

    sys_close(root_fd);

    [7] if (new_decode_dev(real_root_dev) == Root_RAM0) {

    sys_chdir("/old");

    return;

    }

    [8] ROOT_DEV = new_decode_dev(real_root_dev);

    mount_root();

    [9] printk(KERN_NOTICE "Trying to move old root to /initrd ... ");

    error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);

    if (!error)

    printk("okay/n");

    else {

    int fd = sys_open("/dev/root.old", O_RDWR, 0);

    if (error == -ENOENT)

    printk("/initrd does not exist. Ignored./n");

    else

    printk("failed/n");

    printk(KERN_NOTICE "Unmounting old root/n");

    sys_umount("/old", MNT_DETACH);

    printk(KERN_NOTICE "Trying to free ramdisk memory ... ");

    if (fd < 0) {

    error = fd;

    } else {

    error = sys_ioctl(fd, BLKFLSBUF, 0);

    sys_close(fd);

    }

    printk(!error ? "okay/n" : "failed/n");

    }

    }

    代码[1]:real_root_dev为一个全局变量,用来保存realfs的设备号。

    代码[2]:调用mount_block_root将realfs加载到VFS的/root下。

    代码[3]:提取rootfs的根文件描述符并将其保存到root_fd,资料中提及其用处就是在后续调用sys_chroot到initrd的文件系统后,处理完init请求后,还能够再次切回到rootfs,这一点在一份IBM官方有关cpio-initrd和image-initrd的执行流程图中可以看到,如下:

    clip_image004

    代码[4]:sys_chroot到initrd文件系统,前面已经挂载initrd到VFS的root目录下;

    代码[5]:执行initrd中的linuxrc,并等待执行结束;

    代码[6]:initrd执行结束后,切回到rootfs,不知道为什么直接用节点切呢?

    代码[7]:如果real_root_dev直接配置为Root_RAM0,也即直接使用直接使用initrd作为realfs,改变当前目录到initrd中,并直接返回。

    代码[8]:执行完Linuxrc后,realfs已经确定,则调用mount_root将realfs挂载到VFS的/root目录下,并将当前的目录配置为VFS的/root。

    代码[9]:收尾的工作,例如释放内存等。

    4. 真实根文件系统挂载后的操作

    下面回过头来再看上面提到的init_post,该函数实际上是在Kernel_init中最后执行的函数。其代码如下:

    /* This is a non __init function. Force it to be noinline otherwise gcc

    * makes it inline to init() and it becomes part of init.text section

    */

    static noinline int init_post(void)

    {

    /* need to finish all async __init code before freeing the memory */

    async_synchronize_full();

    free_initmem();

    unlock_kernel();

    mark_rodata_ro();

    system_state = SYSTEM_RUNNING;

    numa_default_policy();

    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

    printk(KERN_WARNING "Warning: unable to open an initial console./n");

    (void) sys_dup(0);

    (void) sys_dup(0);

    current->signal->flags |= SIGNAL_UNKILLABLE;

    if (ramdisk_execute_command) {

    run_init_process(ramdisk_execute_command);

    printk(KERN_WARNING "Failed to execute %s/n",

    ramdisk_execute_command);

    }

    /*

    * 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) {

    run_init_process(execute_command);

    printk(KERN_WARNING "Failed to execute %s. Attempting "

    "defaults.../n", execute_command);

    }

    run_init_process("/sbin/init");

    run_init_process("/etc/init");

    run_init_process("/bin/init");

    run_init_process("/bin/sh");

    panic("No init found. Try passing init= option to kernel.");

    }

    可以看到,在该函数的最后,以此会去搜索文件并执行ramdisk_execute_command、execute_command、/sbin/init、/etc/init、/bin/init和/bin/sh,如果发现这些文件均不存在的话,则通过panic输出错误命令,并将当前的系统Halt在那里。

    四.RootFS加载过程流程图描述

    详细的流程图如下:

    clip_image006

    (完)


    [p1]之所以称之为“某一个文件”,是因为这里文件的名字因为操作系统的版本不同而不同

    展开全文
  • 根文件系统挂载过程—基于linux3.10

    千次阅读 2015-05-29 18:38:47
    本文基于linux3.10某一...图1.1 根文件系统配置选项设置  两行配置如下: [*] Initial RAMfilesystem and RAM disk (initramfs/initrd) support (usr/rootfs.cpio.gz)Initramfs source file(s) 这两行的意义是
  • Linux 文件系统挂载与开机自动挂载

    万次阅读 2018-03-15 20:27:16
    1. 背景  前博 介绍了如何配置Linux的Samba服务以便... 对于Linux系统,根文件系统“/”之外的其他文件要想能够被访问,都必须通过“关联”至根文件系统上的某个目录来实现,此关联操作即为“挂载”,此目录即为
  • linux下文件目录为树状结构,文件系统挂载在虚拟系统的VFS各个目录下。 VFS是Linux中的一个虚拟文件文件系统,也称为虚拟文件系统交换层(Virtual Filesystem Switch),是一种软件机制。它为应用程序员提供一层抽象...
  • Linux根文件系统的制作什么是文件系统计算机的文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易,文件系统使用文件和树形目录的抽象逻辑概念代替了硬盘和光盘等物理设备使用数据块的概念,...
  • 本文旨在查找相关资料的基础上梳理一下老师上课的内容,巩固学习。 一、定义概述 挂载Linux 中将一个文件系统...根文件系统挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbi...
  • NFS挂载嵌入式Linux根文件系统

    千次阅读 2011-01-17 20:31:00
     之前转载过一篇《nfs挂载android根文件系统》,但是自己一直都没做成功过,之前因为挂载普通嵌入式Linux根文件系统总是在内核刚启动时,开发板就不向串口打印信息了,而是直接打印到开发板lcd屏上,我想这...
  • 所有的硬件设备必须挂载之后才能使用,只...在 Linux看来,任何硬件设备也都是文件,它们各有自己的一套文件系统(文件目录结构)。 因此产生的问题是,当在 Linux 系统中使用这些硬件设备时,只有将Linux本身的文件目
  • Linux最小根文件系统支持自动挂载U盘TF卡。通过 mdev 工具实现 U 盘和 SD/TF 卡的自动挂载
  • Linux根文件系统制作

    2018-10-17 20:18:46
    该文档主要用于详细介绍Linux根文件系统制作过程,可参考步骤流程
  • openwrt 将根文件系统挂载在U盘上

    千次阅读 2015-10-17 15:19:14
    wr703n 搭载openwrt系统,将根文件系统挂载在U盘上。
  •  根文件系统首先是一种文件系统,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把...
  • NFS允许一个系统在网络上与他人共享目录和文件,通过使用NFS,用户和程序可以像访问本地文件一样访问远端系统上的文件,下面介绍linux系统之间通过nfs网络文件系统挂载的设置方法
  • 根文件系统挂载

    千次阅读 2012-06-23 09:08:19
    如果在 populate_rootfs() 中成功地 unpack_to_rootfs() 的话,之后内核就不会再对 initrd 作任何操作,也不会去挂载根文件系统,所有的工作都留给 cpio 包(也就是rootfs)中的 /init 去完成了。关于这一点,在后面...
  • linux 根文件系统

    2011-04-12 14:08:30
    文件系统挂载.linux文件系统,ETX3文件系统,ETX2文件系统,CF卡文件系统
  • Linux中Root Filesystem(根文件系统)是必不可少的,常用的是BusyBox,本节就介绍一下使用BusyBox制作Linux根文件系统rootfs,主要参考xilinx wiki上Zynq Root FileSystem Creation方法:...
  • Linux根文件系统介绍

    千次阅读 2013-07-10 08:52:42
    系统 根文件系统首先是一种文件系统,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中...
  • root=/dev/mmcblk0p2根文件系统挂载

    千次阅读 2020-03-08 16:04:21
    linux挂载文件系统,一般通过ramdisk方式。如果没有使用ramdisk方式,而是使用磁盘上分区文件系统,那么在prepare_namespace中进行。 static noinline void __init kernel_init_freeable(void) { /* * Wait ...
  • Linux文件系统挂载过程代码分析

    千次阅读 2017-03-09 01:02:50
    linux系统中是可以通过mount挂载一个文件系统 mount命令格式:mount [-t vfstype] [-o options] device dir1.-t vfstype 指定文件系统的类型,通常不必指定。mount 会自动选择正确的类型。常用类型有: 光盘或光盘...
  • 根文件系统挂载过程分析

    千次阅读 2016-04-06 08:35:24
    原文地址:... ...一、内存盘INITRD(INITial Ram Disk)技术 ...在Linux操作系统中,有一项特殊的功能——初始化内存盘INITRD(INITial Ram Disk)技术,而且内核支持压缩的文件系统映像。有了
  • Hadoop支持通过NFSv3挂载HDFS文件系统到本地目录,允许用户像访问本地文件系统一样访问HDFS,对于普通用户来说大大的简化了HDFS的使用。该功能通过引入NFS Gateway服务实现,将NFS协议转换为HDFS访问协议。本篇文章...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 82,379
精华内容 32,951
关键字:

linux根文件系统挂载

linux 订阅