1. 資料結構
vfsmount
struct vfsmount {
struct list_head mnt_hash; // hash表
struct vfsmount*mnt_parent; /* fs we are mounted on */
struct dentry*mnt_mountpoint; /* dentry ofmountpoint */
struct dentry *mnt_root; /* root of the mounted tree */|
struct super_block*mnt_sb; /* pointer to superblock */
struct list_headmnt_mounts; /* list of children,anchored here */
struct list_headmnt_child; /* and going throughtheir mnt_child */
int mnt_flags;
/* 4 bytes hole on 64bitsarches */
const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
struct list_head mnt_list;
struct list_headmnt_expire; /* link in fs-specificexpiry list */
struct list_headmnt_share; /* circular list ofshared mounts */
struct list_headmnt_slave_list;/* list of slave mounts */
struct list_headmnt_slave; /* slave list entry */
struct vfsmount*mnt_master; /* slave is onmaster->mnt_slave_list */
struct mnt_namespace*mnt_ns; /* containingnamespace */
int mnt_id; /*mount identifier */
int mnt_group_id; /* peer groupidentifier */
/*
* We put mnt_count & mnt_expiry_mark atthe end of struct vfsmount
* to let these frequently modified fields in aseparate cache line
* (so that reads of mnt_flags wont ping-pongon SMP machines)
*/
atomic_t mnt_count;
int mnt_expiry_mark; /* true if marked for expiry*/
int mnt_pinned;
int mnt_ghosts;
int mnt_writers;
};
namespace
struct mnt_namespace {
atomic_t count;
struct vfsmount * root;
struct list_head list;
wait_queue_head_t poll;
int event;
};
2. 初始化
void __init mnt_init(void)
init_rwsem(&namespace_sem);
static struct kmem_cache *mnt_cache =
kmem_cache_create("mnt_cache",sizeof(struct vfsmount),
0, SLAB_HWCACHE_ALIGN |SLAB_PANIC, NULL);
static struct list_head * mount_hashtable= (struct list_head *)__get_free_page(GFP_ATOMIC);
for (u = 0; u <HASH_SIZE; u++)
INIT_LIST_HEAD(&mount_hashtable[u]);
// 下面這行是初始化sysfs,放在這裡其實不太好,但由於初始化sysfs時用到了mount操作,而下面做其它檔案系統mount時又用到sysfs,所以也只能放這裡。
sysfs_init();
fs_kobj =kobject_create_and_add("fs", NULL); //在sysfs中加上fs節點
init_rootfs();
init_mount_tree();
mnt_init最後調用了init_rootfs和init_mount_tree兩個函數,下面分別來看一下。
2.1 init_rootfs
int __init init_rootfs(void)
int err;
err =bdi_init(&ramfs_backing_dev_info);
// 註冊rootfs檔案系統
err =register_filesystem(&rootfs_fs_type);
rootfs_fs_type定義在fs/ramfs/inode.c。
static struct file_system_type rootfs_fs_type = {
.name = "rootfs",
.get_sb = rootfs_get_sb,
.kill_sb =kill_litter_super,
};
2.2 init_mount_tree
static void __init init_mount_tree(void)
// 得到rootfs的vfsmount結構,設定裡面的超級塊等資訊,見下文
structvfsmount *mnt =do_kern_mount("rootfs",0, "rootfs", NULL);
// 根據mnt建立一個mnt_namespace,見下文
structmnt_namespace *ns =create_mnt_ns(mnt);
init_task.nsproxy->mnt_ns= ns;
get_mnt_ns(ns);
root.mnt= ns->root;
root.dentry= ns->root->mnt_root;
// 由於調init_mount_tree時init進程還沒啟動,下面的current宏由最開始時設定的棧指標決定,應該就是init_task的taskstruct結構體。
set_fs_pwd(current->fs,&root);
set_fs_root(current->fs,&root);
do_kern_mount的作用是分配一個vfsmount,同時裡面初始化了super block。
struct vfsmount * do_kern_mount(const char *fstype, intflags, const char *name, void *data)
structfile_system_type *type = get_fs_type(fstype);
structvfsmount *mnt;
mnt =vfs_kern_mount(type, flags, name, data);
put_filesystem(type);
returnmnt;
struct vfsmount * vfs_kern_mount(struct file_system_type*type, int flags, const char *name, void *data)
structvfsmount * mnt = alloc_vfsmnt(name);
type->get_sb(type,flags, name, data, mnt);
//mnt_mountpoint設定為mnt_root,即掛載點為自己
mnt->mnt_mountpoint= mnt->mnt_root;
// mnt_parent設成自己
mnt->mnt_parent= mnt;
struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
structmnt_namespace *new_ns = alloc_mnt_ns();
mnt->mnt_ns= new_ns;
new_ns->root= mnt;
list_add(&new_ns->list,&new_ns->root->mnt_list);
3. 檔案系統載入
mount操作包括remount,move_mount, mount塊裝置,mountloop裝置等,都由核心功能do_mount完成。本質上,mount操作的過程就是建立一個vfsmount結構,然後將此結構和掛載點關聯。關聯之後,目錄尋找時就能沿著vfsmount掛載點一級級向下尋找檔案了。
關於如何經過vfsmount尋找檔案,可以看函數kern_path。kern_path的作用為根據路徑得到dentry和inode結構,裡面會調用到path_walk,最終調用__follow_mount。
下面看一下mount塊裝置的實現。
long do_mount(char *dev_name, char *dir_name, char *type_page,
unsignedlong flags, void *data_page)
struct path path;
// 得到掛載點的vfsmount和dentry結構,儲存在structpath中。
kern_path(dir_name,LOOKUP_FOLLOW, &path);
。。。
// 對塊裝置,調用到do_new_mount
do_new_mount(&path, type_page, flags,mnt_flags, dev_name, data_page);
static int do_new_mount(struct path *path, char *type, int flags, int mnt_flags, char*name, void *data)
struct vfsmount * mnt =do_kern_mount(type, flags, name, data);
do_add_mount(mnt, path,mnt_flags, NULL);
int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags, structlist_head *fslist)
// 找到該目錄的當前掛載點的vfsmount和dentry結構
while (d_mountpoint(path->dentry)&& follow_down(path)) ;
// graft_tree主要作用是將新的vfsmount加到mounttree。
graft_tree (newmnt, path);
loopmount與mount塊裝置類似,根據被載入裝置的superblock、dentry結構建立一個vfsmount結構,將該vfsmount掛到mounttree上。