Chinaunix首页 | 论坛 | 博客
  • 博客访问: 320872
  • 博文数量: 86
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 185
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-25 17:14
个人简介

代码才是最叼的

文章分类

全部博文(86)

文章存档

2019年(1)

2018年(1)

2017年(9)

2016年(19)

2015年(55)

2014年(1)

分类: LINUX

2019-03-01 08:52:17

一、rootfs的诞生

Linux一切皆文件的提出:在Linux中,普通文件、目录、字符设备、块设备、套接字等都以文件被对待;他们具体的类型及其操作不同,但需要向上层提供统一的操作接口。虚拟文件系统VFS就是Linux内核中的一个软件层,向上给用户空间程序提供文件系统操作接口;向下允许不同的文件系统共存。所以,所有实际文件系统都必须实现VFS的结构封装。

Linux系统中任何文件系统的挂载必须满足两个条件:挂载点和文件系统。直接挂载nfsflash文件系统有如下两个问题必须解决:

1.谁来提供挂载点?我们可以想象自己创建一个超级块(包含目录项和i节点),这时挂载点不是就有了吗;很可惜,linux引入VFS(一切皆文件,所有类型文件系统必须提供一个VFS的软件层、以向上层提供统一接口)后该问题不能这么解决,因为挂载点必须关联到文件系统、也就是说挂载点必须属于某个文件系统。

2.怎样访问到nfsflash上的文件系统?我们可以说直接访问设备驱动读取其上边的文件系统(设备上的文件系统是挂载在自己的根目录),不就可以了吗;别忘了还是LinuxVFS,设备访问也不例外。因为访问设备还是需要通过文件系统来访问它的挂载点,不能直接访问(要满足LinuxVFS架构,一切皆文件)

所以,一句话:rootfs之所以存在,是因为需要在VFS机制下给系统提供最原始的挂载点。

如此矛盾,需要我们引入一种特殊文件系统:

1.它是系统自己创建并加载的第一个文件系统;该文件系统的挂载点就是它自己的根目录项。

2.该文件系统不能存在于nfsflash上,因为如此将会陷入之前的矛盾。

rootfs的诞生:

上述问题需要我们创建具有如下三个特点的特殊文件系统:

1.它是系统自己创建并加载的第一个文件系统;

2.该文件系统的挂载点就是它自己的根目录项对象;

3.该文件系统仅仅存在于内存中。

  由以上分析可以看出,rootfsLinuxVFS(一切皆文件,所有类型文件系统必须提供一个VFS的软件层、以向上层提供统一接口)存在的基石;二者关系密切。如果没有VFS机制,rootfs也就没有存在的必要;同样,如果没有rootfsVFS机制也就不能实现。

 

  其实,VFS是一种机制、是Linux下每一种文件系统(包括刚才说的rootfs,还有常见的ext3yaffs等)都必须按照这个机制去实现的一种规范;而rootfs仅仅是符合VFS规范的而且又具有如上3个特点的一个文件系统。

  VFSLinux文件系统实现必须遵循的一种机制,rootfs是一种具体实现的文件系统、Linux下所有文件系统的实现都必须符合VFS的机制(符合VFS的接口);这就是二者的真正关系。

 

以下分析基于Android模拟器Linux2.6.29内核:

 

二、相关数据结构

 

Linux内核中current指针作为全局变量,使用非常广泛;例如:进程上下文中获取当前进程ID、任务调度,以及open等文件系统调用中路径搜索等;首先介绍下current结构体:

 

各个平台、各个内核版本中current的实现可能不同;但原理是一样的。该指针一般定义在具体平台的current.h头文件中,类型为struct task_struct

 

#define current (get_current())

static inline struct task_struct *get_current(void)

include/linux/sched.h

 

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;

  ......

}

1.文件系统注册

 

kernel/include/include/fs.h

 

struct file_system_type {

  const char *name; //文件系统名字;如:rootfsext3

  int fs_flags;

  int (*get_sb) (struct file_system_type *, int, const char *, void *, struct vfsmount *);

  //安装/挂载文件系统时,会调用;获取超级块。

  void (*kill_sb) (struct super_block *);

  //卸载文件系统时会调用。

  struct module *owner;

  struct file_system_type * next;

  //指向下一个文件系统类型。

  struct list_head fs_supers;

  //同一个文件系统类型中所有超级块组成双向链表。

  struct lock_class_key s_lock_key;

  struct lock_class_key s_umount_key;

 

  struct lock_class_key i_lock_key;

  struct lock_class_key i_mutex_key;

  struct lock_class_key i_mutex_dir_key;

  struct lock_class_key i_alloc_sem_key;

};

2.文件系统挂载vfsmount(struct 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;

};

3.超级块(struct super_bloc)

 

kernel/include/linux/fs.h

 

struct super_block {

  struct list_head s_list; /* Keep this first */

  dev_t s_dev; /* search index; _not_ kdev_t */

  unsigned long s_blocksize;

  unsigned char s_blocksize_bits;

  unsigned char s_dirt;

  unsigned long long s_maxbytes; /* Max file size */

  struct file_system_type *s_type; //文件系统类型

  //(kernel/include/linux/fs.h,struct file_system_type)

  const struct super_operations *s_op;

  struct dquot_operations *dq_op;

  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;

  struct mutex s_lock;

  int s_count;

  int s_need_sync_fs;

  atomic_t s_active;

#ifdef CONFIG_SECURITY

  void                    *s_security;

#endif

  struct xattr_handler **s_xattr;

 

  struct list_head s_inodes; /* all inodes */

  struct list_head s_dirty; /* dirty inodes */

  struct list_head s_io; /* parked for writeback */

  struct list_head s_more_io; /* parked for more writeback */

  struct hlist_head s_anon;//哈希表头 /* anonymous dentries for (nfs) exporting */

  struct list_head s_files;

  /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */

  struct list_head s_dentry_lru; /* unused dentry lru */

  int s_nr_dentry_unused; /* # of dentry on lru */

 

  struct block_device *s_bdev;

  struct mtd_info *s_mtd;

  struct list_head s_instances;

  struct quota_info s_dquot; /* Diskquota specific options */

 

  int s_frozen;

  wait_queue_head_t s_wait_unfrozen;

 

  char s_id[32]; /* Informational name */

 

  void *s_fs_info; /* Filesystem private info */

  fmode_t s_mode;

 

  /*

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

 

  /* Granularity of c/m/atime in ns.Cannot be worse than a second */

  u32    s_time_gran;

 

  /*

  * 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 *s_options;

 

  /*

  * storage for asynchronous operations

  */

  struct list_head s_async_list;

};

4.目录索引节点(struct 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)

 

kernel/include/linux/dcache.h

 

struct dentry {

  atomic_t 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 */

};

其他:

 

include/linux/fs.h

 

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

};

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,并调用set_fs_root设置系统current的根文件系统为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挂载到自己的根目录;设置系统current根目录和根文件系统

 

kernel/fs/namespace.c

static void __init init_mount_tree(void)

{

  struct vfsmount *mnt;

  struct mnt_namespace *ns;

  struct path root;

  //创建rootfsvfsmount结构,建立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;

 

  //设置系统currentpwd目录和文件系统

  set_fs_pwd(current->fs, &root);

  //设置系统current根目录,根文件系统。这个是关键!!!整个内核代码最多只有两处调用

  set_fs_root(current->fs, &root);  

}

以下着重分析do_kern_mount函数,它实现了rootfs在自己根目录上的挂载:

 

kernel/fs/super.c

struct vfsmount *

do_kern_mount(const char *fstype, int flags, const char *name, void *data)

{

  mnt = vfs_kern_mount(type, flags, name, data);

  return mnt;

}

kernel/fs/super.c

 

struct vfsmount *

vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)

{

  mnt = alloc_vfsmnt(name); //建立并填充vfsmount

  error = type->get_sb(type, flags, name, data, mnt);//为文件系统建立并填充超级块(主要是其dentryinode),建立rootfs根目录

  mnt->mnt_mountpoint = mnt->mnt_root; //文件系统挂载点目录,其实就是刚才建立的”/”目录。挂载点就是自己!!!!

  mnt->mnt_parent = mnt; //父对象是自己!!!!

  return mnt;

}

kernel/fs/ramfs/inode.c

static int rootfs_get_sb(struct file_system_type *fs_type,

int flags, const char *dev_name, void *data, struct vfsmount *mnt)

{

  return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super,

    mnt);

}

kernel/fs/super.c

 

int get_sb_nodev(struct file_system_type *fs_type,

int flags, void *data,

int (*fill_super)(struct super_block *, void *, int),

struct vfsmount *mnt)

{

  struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);

  //在内存中分配一个超级块

  error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);

  //执行回调,填充超级块,并建立根目录项及对应i节点

  /*

  kernel/fs/ramfs/inode.c

  static int ramfs_fill_super(struct super_block * sb, void * data, int silent)

  {

    struct inode * inode;

    struct dentry * root;

    sb->s_maxbytes = MAX_LFS_FILESIZE;

    sb->s_blocksize = PAGE_CACHE_SIZE;

    sb->s_blocksize_bits = PAGE_CACHE_SHIFT;

    sb->s_magic = RAMFS_MAGIC;

    sb->s_op = &ramfs_ops;

    //static const struct super_operations ramfs_ops;

    sb->s_time_gran = 1;

    //建立根目录索引节点,我们最终的目标是要找到目录项对象关联的索引节点。

    //根目录索引节点会有自己的ops

    inode = ramfs_get_inode(sb, S_IFDIR | 0755, 0);

    //ramfs_get_inode

    kernel/fs/ramfs/inode.c

    struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)

    {

      struct inode * inode = new_inode(sb);

      switch (mode & S_IFMT) {  //判断文件类型

        default:

  init_special_inode(inode, mode, dev);

  //init_special_inode

          void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)

          {

    inode->i_mode = mode;

    if (S_ISCHR(mode)) {//字符设备文件

      inode->i_fop = &def_chr_fops;

      inode->i_rdev = rdev;

    } else if (S_ISBLK(mode)) {//块设备文件

      inode->i_fop = &def_blk_fops;

      inode->i_rdev = rdev;

    } else if (S_ISFIFO(mode))

      inode->i_fop = &def_fifo_fops;

    else if (S_ISSOCK(mode)) //网络设备文件

      inode->i_fop = &bad_sock_fops;

    else

      printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"

  " inode %s:%lu\n", mode, inode->i_sb->s_id,

  inode->i_ino);

           }

          //init_special_inode end

          break;

        case S_IFREG: //普通文件

          inode->i_op = &ramfs_file_inode_operations;  //索引节点的操作方法

          inode->i_fop = &ramfs_file_operations;  //缺省普通文件的操作方法

          break;

        case S_IFDIR:  //目录文件

          inode->i_op = &ramfs_dir_inode_operations;

          //ramfs_dir_inode_operations

          static const struct inode_operations ramfs_dir_inode_operations;

          kernel/include/linux/fs.h

          struct inode_operations {

            int (*create) (struct inode *,struct dentry *,int, struct nameidata *);

            int (*mkdir) (struct inode *,struct dentry *,int);

    int (*rmdir) (struct inode *,struct dentry *);

    int (*mknod) (struct inode *,struct dentry *,int,dev_t);

          }

          //ramfs_dir_inode_operations end

          inode->i_fop = &simple_dir_operations;  //目录文件的操作方法

          inc_nlink(inode);

          break;

      }

    }

    //ramfs_get_inode end

    //建立根目录目录对象,目录项对象的存在主要是为了我们进行路径的查找。

    root = d_alloc_root(inode);   

    //d_alloc_root

    kernel/fs/dcache.c

    struct dentry * d_alloc_root(struct inode * root_inode)

    {

      struct dentry *res = NULL;

      static const struct qstr name = { .name = "/", .len = 1 };

      res = d_alloc(NULL, &name);

      res->d_sb = root_inode->i_sb; //指向该文件系统的超级块

      res->d_parent = res;  //根目录的父亲是它自己

      d_instantiate(res, root_inode); //关联 dentry inode

    }

    //d_alloc_root end

    sb->s_root = root;  //超级块的s_root指向刚建立的根目录对象。

  }

  */

  return simple_set_mnt(mnt, s); //关联超级块(包含目录项dentryi节点inode)vfsmount

}

kernel/fs/namespace.c

int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb)

{

  printk("TK-------_>>>>>>>namespace.c>>>>simple_set_mnt\n");//add by tankai

  mnt->mnt_sb = sb;  //mnt_sb超级块指针附值

  mnt->mnt_root = dget(sb->s_root); //mnt_root指向的根目录赋值

  return 0;

}

至此,rootfs文件系统建立、并且挂载于自己超级块(包括目录项dentryi节点inod)对应的目录项,设置了系统current根目录和根文件系统、pwd的目录和文件系统。

========================================

 

释放Initramfsrootfs;如果Initramfs中有init,这种情况比较特殊、rootfs就是最后系统使用的根文件系统。

 

而且此时,不需要在单独烧录根文件系统的img;此时,根文件系统就是内核uImage的一部分。当然,缺陷就是该文件系统运行时的介质是ramdisk即内存盘、它不再与磁盘对应;因此,此时修改根目录下的文件将不被得到保存。它的内核配置项为:CONFIG_INITRAMFS_SOURCE。实际项目中会经常碰到。

 

make menuconfig->General setup->Initial RAM filesystem and RAM disk(initramfs/initrd) support

底下的Initramfs source file(s)填写根文件系统的路径,如:../out/target/product/tclm6/root;不填的话,将导致initrd或磁盘文件系统的挂载(因为下边将会看到,内核将找不到“/init”)。

 

对应内核源码:

 

kernel/init/main.c

static int __init kernel_init(void * unused){

  ......

  do_basic_setup();  //初始化设备驱动,加载静态内核模块;释放Initramfsrootfs

  /*

  kernel/init/initramfs.c

  rootfs_initcall(populate_rootfs);

  static int __init populate_rootfs(void)

  {

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

    err = unpack_to_rootfs((char *)initrd_start,

                        initrd_end - initrd_start, 1); //释放ramdiskrootfs

  }

  */

  ......

  if (!ramdisk_execute_command)  ramdisk_execute_command = "/init";

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

    ramdisk_execute_command = NULL;

    //如果此时rootfs中没有init,则加载initfdnfs或磁盘文件系统

    //也即磁盘的文件系统挂载至rootfs/root目录,并设置系统current对应的根目录项为磁盘根目录项、系统current根文件系统为磁盘文件系统

    //至此,rootfs对于以后所有进程而言、已被隐藏。

    prepare_namespace();

  }

  init_post(); //启动init进程

  ......

}

看看init_post实现:

 

static noinline int init_post(void)

{

  if (ramdisk_execute_command) {  //Initramfs从这里启动init

    run_init_process(ramdisk_execute_command);

    printk(KERN_WARNING "Failed to execute %s\n", ramdisk_execute_command);

  }

  //initrdnfs和磁盘都是从如下启动的init

  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");

}

四、挂载实际文件系统至rootfs,并调用set_fs_root设置为系统current的根文件系统

 

下边从uboot启动内核参数的角度来简单说明:

 

以下三种情况都是将文件系统挂载到rootfs/root目录,并将系统current的根目录切换为/root、系统current的根文件系统切换为磁盘文件系统。

 

kernel/init/do_mounts.c

 

void __init prepare_namespace(void)

{

  if (initrd_load()) //如果挂载initrd并执行成功,则不再挂载磁盘文件系统

    goto out;

  if (saved_root_name[0]) {

    root_device_name = saved_root_name;

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

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

      mount_block_root(root_device_name, root_mountflags); //启动时root=参数,如《四.2》中“root=/dev/mtdblock0

      goto out;

    }

    ROOT_DEV = name_to_dev_t(root_device_name);

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

      root_device_name += 5;

  }

  mount_root(); //将实际文件系统挂载到rootfs/root目录

out:

  //sys_mount(".", "/", NULL, MS_MOVE, NULL); 这句话无关紧要,影响理解;屏蔽不影响功能

  sys_chroot(".");  //将当前目录(/root)设置为系统current根目录,磁盘文件系统设置为系统current根文件系统。

}

下边分两步解释mount_root()sys_chroot(".")调用:

 

1.nfs或磁盘文件系统挂载至rootfs/root目录(以磁盘为例)

 

void __init mount_root(void)

{

  if (mount_nfs_root())  //如果网络文件系统挂载成功,则nfs作为根文件系统

    return;

  //挂载磁盘文件系统为根文件系统

  //rootfs中建立/dev/root设备文件

  create_dev("/dev/root", ROOT_DEV);  //rootfs中建立/dev/root设备文件,也就是/dev/mtdblock0设备。

  //挂载/dev/rootrootfs/root目录

  mount_block_root("/dev/root", root_mountflags);

}

void __init mount_block_root(char *name, int flags)

{

  int err = do_mount_root(name, p, flags, root_mount_data);  

}

static int __init do_mount_root(char *name, char *fs, int flags, void *data)

{

  int err = sys_mount(name, "/root", fs, flags, data);///dev/root挂载到/root

  sys_chdir("/root"); //系统current->fs->pwd为当前目录/root

  ROOT_DEV = current->fs->pwd.mnt->mnt_sb->s_dev;

  return 0;

}

2.将当前目录/root设置为系统current根目录,磁盘文件系统设置为系统current根文件系统

 

分析sys_chroot("."):见Linux内核编程之C语言预处理功能与宏

 

fs/open.c

 

SYSCALL_DEFINE1(chroot, const char __user *, filename)

{

  struct path path;

  error = user_path_dir(filename, &path);

  //这才是完成切换的关键!!!!整个内核代码只有两处调用

  set_fs_root(current->fs, &path);

}

注意,如下情况:rootfs特殊文件系统没有被卸载,他只是隐藏在基于磁盘的根文件系统下了。

 

initrd作为根文件系统

 

setenv bootargs root=/dev/ram0 initrd=0x2800000,24M rootfstype=ext2 mem=64M console=ttyAMA0

 

参数说明:

 

root:用来指定rootfs的位置。

 

rootfstype:用来指定文件系统的类型。

 

nfs作为根文件系统

 

setenv bootargs root=/dev/nfs nfsroot=192.168.1.7:/opt/yz/nfs,rw ip=192.168.1.160 mem=64M console=ttyAMA0

 

参数说明:

 

nfsroot:文件系统在哪台主机的哪个目录下。

 

ip:指定系统启动之后网卡的ip地址。

 

flash作为根文件系统

 

setenv bootargs root=/dev/mtdblock0 mem=16M mtdparts=armflash.1:4M@0x400000(jffs2) macaddr=9854 rootfstype=jffs2 console=ttyAMA0

参数说明:

mtdparts:根文件系统在flash中的位置。

总结:rootfs永远不会被卸载,它只是被隐藏了。在用户空间下,更多地情况是只能见到rootfs这棵大树的一叶,而且还是被安装过文件系统了的。

 

阅读(3186) | 评论(0) | 转发(0) |
0

上一篇:MSM 平台modem相关理解

下一篇:没有了

给主人留下些什么吧!~~