Chinaunix首页 | 论坛 | 博客
  • 博客访问: 67712
  • 博文数量: 25
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-25 16:11
个人简介

生命是个奇迹,我用时间来证明!

文章分类
文章存档

2013年(25)

我的朋友

分类:

2013-05-15 10:36:23

文件系统挂载

Linux也是用系统的根文件系统:它由内核在引导阶段直接安装,并拥有系统初始化脚本以及最基本的系统程序。作为一个目录树,每个文件系统都拥有自己的根目录(root directory)。安装文件系统的目录称为挂载点(mount point)。已安装文件系统属于安装目录的一个子文件系统。已安装文件系统的整个子树位于安装点之下。


已装载文件系统命名空间

在传统的Unix系统中,只有一个已安装文件系统树:从系统的根文件系统开始,每个进程通过指定合适的路径名可以访问已安装文件系统的任何文件。也就是说根目录/是固定的,对应于最开始设置的dentryinode和设备。而linux2.6内核,则每一个进程可拥有自己的已安装文件系统树——叫做进程的已装载文件系统命名空间(mnt_namespace)。

 

通常大多数进程共享同一个已装载文件系统命名空间,相同的已安装文件系统树,即位于系统的根文件系统且被init进程使用的已安装文件系统树。不过,如果clone()系统调用以CLONE_NEWNS标志创建一个新进程,那么进程将获取一个新的已装载文件系统命名空间。这个新的已装载文件系统命名空间随后由子进程继承。

 

当进程安装或卸载一个文件系统时,仅修改它的已装载文件系统命名空间。因此,所做的修改对共享同一已装载文件系统命名空间的所有进程是可见,并且也只对它们可见。进程可以通过系统调用pivot_root()来改变它的已装载文件系统命名空间的根文件系统。

 

进程用一个统一的nsproxy结构来管理它的各种命名空间:

struct task_struct {

……

/* filesystem information */

    struct fs_struct *fs;

/* open file information */

    struct files_struct *files;

/* namespaces */

    struct nsproxy *nsproxy;

……

};

 

进程的命名空间由进程描述符的nsproxy字段指向的nsproxy结构描述:

---------------------------------------------------------------------

include/linux/ns_proxy.h

struct nsproxy {

    atomic_t count;

    struct uts_namespace *uts_ns;

    struct ipc_namespace *ipc_ns;

    struct mnt_namespace *mnt_ns;

    struct pid_namespace *pid_ns;

    struct net         *net_ns;

};

---------------------------------------------------------------------

而进程的已装载文件系统命名空间则由nsproxy结构的mnt_ns字段指向的mnt_namespace结构描述:

---------------------------------------------------------------------

include/linux/mnt_namespace.h

struct mnt_namespace {

/* 引用计数器(共享已安装文件系统命名空间的进程数)*/

    atomic_t      count;

    struct vfsmount * root; /* 已安装文件系统描述符 */

    struct list_head  list; /* 所有已安装文件系统描述符表的头 */

    wait_queue_head_t poll;

    int event;

};

---------------------------------------------------------------------

List字段是双向链表的头,该表聚集了属于已安装文件系统命名空间的所有已安装文件系统。root字段表示已安装文件系统,它是这个命名空间的已安装文件系统的根目录。

 

文件系统的挂载

对于大多数传统的Unix系统来说,一个文件系统只能挂载一次,而对于linux,则同一个文件系统可以被多次挂载。但不管被挂载多少次,它又的的确确是唯一的,都仅有一个超级快对象。

挂载的文件系统形成一个层次:一个文件系统的挂载点可能是另一个文件系统的目录,而第二个文件系统又挂载在第三个文件系统上,等等。把多个文件系统挂载在一个单独的挂在点上也是可能的。已经使用先前安装下的文件和目录的进程可以继续正常执行,但在同一挂载点上新的安装隐藏前面挂载的文件系统。当最顶层的安装被卸载时,下一层的挂载操作再一次变得可见。

对于每个挂载操作,内核必须在内存中保存挂载点和挂载标志,以及要挂载文件系统与其他已挂载文件系统之间的关系。这样的信息保存在挂载文件系统描述符中;每个描述符是一个vfsmount类型的结构体,其定义如下:

---------------------------------------------------------------------

include/linux/mount.h

struct vfsmount {

    struct list_head mnt_hash; /* 用于散列链表的指针 */

    /* 指向父文件系统,这个文件系统挂载在其上 */

    struct vfsmount *mnt_parent;    /* fs we are mounted on */

    /* 指向本文件系统挂载点目录的dentry */

    struct dentry *mnt_mountpoint;  /* dentry of mountpoint */

    /* 指向挂载目录树根目录的dentry */

    struct dentry *mnt_root; /* root of the mounted tree */

    struct super_block *mnt_sb; /* pointer to superblock */

    /* 包含所有文件系统描述符链表的头(相对于这个文件系统)*/

    struct list_head mnt_mounts;    /* list of children, anchored here */

    /* 用于已安装文件系统链表mnt_mounts的指针 */

    struct list_head mnt_child; /* and going through their mnt_child */

    int mnt_flags;

    /* 4 bytes hole on 64bits arches */

    const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */

    /* 已安装文件系统描述符的mnt_namespace链表的指针 */

    struct list_head mnt_list;

   

    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; /* 引用计数器 */

    /* 如果文件系统标记为到期,那么就设置该标志为true(如果设置了该标志,并且没有任何人使用它,那么它就可以自动卸载这个文件系统)*/

    int mnt_expiry_mark;     /* true if marked for expiry */

    int mnt_pinned;

    int mnt_ghosts;

#ifdef CONFIG_SMP

    int __percpu *mnt_writers;

#else

    int mnt_writers;

#endif

};

---------------------------------------------------------------------

vfsmount结构会被保存在几个双向循环链表中:

由父文件系统vfsmount描述符的地址和挂载点目录的目录项对象的地址索引的散列表。散列表存放在mount_hashtable数组(fs/namespace.c, static struct list_head *mount_hashtable __read_mostly;)中,其大小取决于系统中RAM的容量。表中每一项是具有同一散列值的所有描述符形成的双向循环链表的头。描述符的mnt_hash字段包含指向链表中相邻元素的指针。

对于每一个已挂载文件系统命名空间,所有属于此命名空间的已挂载文件系统描述符形成了一个双向链表。mnt_namespace结构的list字段存放链表的头,vfsmount描述符的mnt_list字段包含链表中指向相邻元素的指针。

对于每一个已安装的文件系统,所有已安装的子文件系统形成了一个双向循环链表。链表的头存放在vfsmount描述符的mnt_mounts字段;子描述符的mnt_child字段存放指向链表中相邻元素的指针。vfsmount_lock自旋锁(fs/namespace.c, __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);)保护vfsmount对象的链表免受同时访问。

 

描述符的mnt_flags字段存放几个标志的值,用以指定如何处理已安装文件系统中的某些种类的文件。这些标志可通过mount命令的选项进行设置。所有的标志定义如下:

---------------------------------------------------------------------

include/linux/mount.h

/* 在已安装的文件系统中禁止setuidsetgid标志*/

#define MNT_NOSUID   0x01

#define MNT_NODEV 0x02 /* 在已安装的文件系统中禁止访问设备文件*/

#define MNT_NOEXEC   0x04 /* 在已安装文件系统中禁止程序执行 */

#define MNT_NOATIME  0x08

#define MNT_NODIRATIME   0x10

#define MNT_RELATIME 0x20

#define MNT_READONLY 0x40   /* does the user want this to be r/o? */

#define MNT_STRICTATIME 0x80

 

#define MNT_SHRINKABLE   0x100

#define MNT_WRITE_HOLD   0x200

 

#define MNT_SHARED   0x1000 /* if the vfsmount is a shared mount */

#define MNT_UNBINDABLE   0x2000 /* if the vfsmount is a unbindable mount */

/*

 * MNT_SHARED_MASK is the set of flags that should be cleared when a

 * mount becomes shared.  Currently, this is only the flag that says a

 * mount cannot be bind mounted, since this is how we create a mount

 * that shares events with another mount.  If you add a new MNT_*

 * flag, consider how it interacts with shared mounts.

 */

#define MNT_SHARED_MASK  (MNT_UNBINDABLE)

#define MNT_PROPAGATION_MASK    (MNT_SHARED | MNT_UNBINDABLE)

 

 

#define MNT_INTERNAL 0x4000

---------------------------------------------------------------------

 

内核提供了几个用以处理vfsmount描述符的函数:

struct vfsmount *alloc_vfsmnt(const char *name)

分配和初始化一个vfsmount描述符,其定义如下:

---------------------------------------------------------------------

fs/namespace.c

struct vfsmount *alloc_vfsmnt(const char *name)

{

    struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);

    if (mnt) {

       int err;

 

       err = mnt_alloc_id(mnt);

       if (err)

           goto out_free_cache;

 

       if (name) {

           mnt->mnt_devname = kstrdup(name, GFP_KERNEL);

           if (!mnt->mnt_devname)

              goto out_free_id;

       }

 

       atomic_set(&mnt->mnt_count, 1);

       INIT_LIST_HEAD(&mnt->mnt_hash);

       INIT_LIST_HEAD(&mnt->mnt_child);

       INIT_LIST_HEAD(&mnt->mnt_mounts);

       INIT_LIST_HEAD(&mnt->mnt_list);

       INIT_LIST_HEAD(&mnt->mnt_expire);

       INIT_LIST_HEAD(&mnt->mnt_share);

       INIT_LIST_HEAD(&mnt->mnt_slave_list);

       INIT_LIST_HEAD(&mnt->mnt_slave);

#ifdef CONFIG_SMP

       mnt->mnt_writers = alloc_percpu(int);

       if (!mnt->mnt_writers)

           goto out_free_devname;

#else

       mnt->mnt_writers = 0;

#endif

    }

    return mnt;

 

#ifdef CONFIG_SMP

out_free_devname:

    kfree(mnt->mnt_devname);

#endif

out_free_id:

    mnt_free_id(mnt);

out_free_cache:

    kmem_cache_free(mnt_cache, mnt);

    return NULL;

}

---------------------------------------------------------------------

alloc_vfsmnt()函数执行如下操作:

a.    vfsmount对象内存池中分配一个该对象,并初始化为0

b.    调用mnt_alloc_id()为该vfsmount对象分配一个标识符,并设置vfsmount对象的标识符字段mnt_id

c.    初始化vfsmount对象的mnt_devname字段。

d.    vfsmount对象的引用计数设置为1

e.    初始化连接各种链表的list_head

f.    若配置了CONFIG_SMP,则初始化mnt_writers字段,否则该字段设置为0

若成功则返回vfsmount对象,否则返回NULL

 

void free_vfsmnt(struct vfsmount *mnt)

   释放由mnt指向的vfsmount描述符

struct vfsmount *lookup_mnt(struct path *path)

   在散列表中查找一个vfsmount并返回它的地址

阅读(895) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~