虚拟文件系统之文件系统的安装与卸载基础知识

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/05/14/6420103.aspx

特殊文件系统:

当网络和磁盘文件系统能够使用户处理存放在内核之外的信息时,特殊文件系统可以为系统程序员和管理员提供一种容易的方式来操作内核的数据结构并实现操作系统的特殊特征。特殊文件系统不限于物理块设备,然而,内核给每个安装的特殊文件系统分配一个虚拟的块设备,让其主设备号为0而次设备号具有任意值(每个特殊文件系统有不同值)。set_anon_super()函数用于初始化特殊文件系统的超级块;该函数本质上获得一个未使用的次设备号dev,然后用主设备号0和次设备号dev设置新超级块的s_dev字段。而另一个kill_anon_super()函数移走特殊文件系统的超级块。unnamed_dev_idr变量包含一个辅助结构(记录当前使用的次设备号)的指针。尽管有些内核设计者不喜欢虚拟块设备标识符,但这些标识符有助于内核以统一的方式处理特殊文件系统和普通文件系统。

文件系统类型注册:

每个注册的文件系统都用一个类型为file_system_type的对象来表示:

struct file_system_type { const char *name; //文件系统名 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; };

fs_supers字段表示给定类型的已安装文件系统所对应的超级块链表的头,链表元素的向后和向前链接存放在超级块对象的s_instances字段中。在系统初始化期间,调用register_filesystem()函数来注册编译时指定的每个文件系统;该函数把相应的file_system_type对象插入到文件系统类型的链表中。

get_fs_type()函数(接受文件系统名作为它的参数)扫描已注册的文件系统链表以查找文件系统类型的name字段,并返回指向相应的file_system_type对象(如果存在)的指针。

命名空间:

每个进程可拥有自己的已安装文件树——叫做进程的命名空间(namespace);对于每一个进程都有自己的namespace,这可以理解为这个进程的地盘。在这里,所有的文件系统都要挂上来统一管理。

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

struct mnt_namespace { atomic_t count; //引用计数器 struct vfsmount *root; //命名空间根目录的已安装文件系统描述符 struct list_head list; //所有已安装文件系统描述符链表的头 wait_queue_head_t poll; int event; };

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

文件系统的根目录有可能不同于进程的根目录:进程的根目录是与“/”路径对应的目录。缺省情况下,进程的根目录与系统的根文件系统的根目录一致(更准确的说是与进程的命名空间中的根文件系统的根目录一致),但是可以通过chroot()系统调用改变进程的根目录。

Linux有所不同,同一个文件系统被安装多次是可能的。当然,如果一个文件系统被安装了n次,那么它的根目录就可通过n个安装点来访问。尽管同一文件系统可以通过不同的安装点来访问,但是文件系统的的确确是唯一的。因此,不管文件系统被安装了多少次,都仅有一个超级块对象。

把多个安装堆叠在一个单独的安装点上也是可能的。尽管已经使用先前安装下的文件和目录的进程可以继续使用,但在同一安装点上的新安装隐藏前一个安装的文件系统。当最顶层的安装被删除时,下一层的安装再一次变为可见的。

已安装文件系统描述符vfsmount

struct vfsmount { struct list_head mnt_hash; //用于散列表链表的指针 struct vfsmount *mnt_parent; //指向父文件系统,这个文件系统安装在其上 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; //用于已安装文件系统链表mnt_mounts的指针 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; 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; };

vfsmount数据结构保存在几个双向链表中:

1. 参照上边命名空间那个图。对于每一个命名空间,所有属于此命名空间的已安装的文件系统描述符形成了一个双向循环链表。namespace结构的list字段存放链表的头,vfsmount描述符的mnt_list字段包含链表中指向相邻元素的指针。

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

3. 对于每一个已安装的文件系统,所有已安装的子文件子系统形成了一个双向循环链表。每个链表的头存放在已安装的文件系统描述符的mnt_mounts字段;此外,描述符的mnt_child字段存放指向链表中相邻元素的指针。

总结一下,三个链表就是这张图了:

1.分配和初始化一个已安装文件系统描述符

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); atomic_set(&mnt->__mnt_writers, 0); } return mnt; out_free_id: mnt_free_id(mnt); out_free_cache: kmem_cache_free(mnt_cache, mnt); return NULL; }

2.释放由mnt指向的已安装文件系统描述符

void free_vfsmnt(struct vfsmount *mnt) { kfree(mnt->mnt_devname); mnt_free_id(mnt); kmem_cache_free(mnt_cache, mnt); }

3.在散列表中查找一个描述符并返回它的地址(参数mnt是父已安装文件系统描述符,dentry表示安装子已安装文件系统描述符的安装点,函数返回该子文件系统的vfsmount)。如果查找到了,增加子已安装文件系统描述符的引用计数。

struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) { struct vfsmount *child_mnt; spin_lock(&vfsmount_lock); if ((child_mnt = __lookup_mnt(mnt, dentry, 1))) mntget(child_mnt); spin_unlock(&vfsmount_lock); return child_mnt; }

lookup_mnt调用__lookup_mnt进行真正的查找,mnt_hashtable是散列表的起始地址,加上偏移hash(mntdentry),就得到散列值相同的链表的头节点。然后进行遍历,如果一个vfsmountmnt_parent域与所给的mnt相同,vfsmount的挂载点的dentry与所给的dentry相同,则就是这个vfsmount

struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, int dir) { struct list_head *head = mount_hashtable + hash(mnt, dentry); struct list_head *tmp = head; struct vfsmount *p, *found = NULL; for (;;) { tmp = dir ? tmp->next : tmp->prev; p = NULL; if (tmp == head) break; p = list_entry(tmp, struct vfsmount, mnt_hash); if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { found = p; break; } } return found; }

增加引用计数:

static inline struct vfsmount *mntget(struct vfsmount *mnt) { if (mnt) atomic_inc(&mnt->mnt_count); return mnt; }

所有文件系统类型的对象都插入到一个单向链表中,由变量file_systems指向链表的第一个元素,而结构中的next字段指向链表的下一个元素。file_systems_lock/写自旋锁保护整个链表免受同时访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值