分类: LINUX
2011-04-05 12:26:36
文件系统可以通过初始化脚本或者用户直接加载,每个文件系统是一棵目录树,所有它有自己根目录。加载文件系统的目录,称为挂载点。
1. Namespace
Linux2.6中,每个进程都有自己挂载的文件系统树,即:进程的命名空间(namespace)。通常很多进程共享某一个命名空间,该命名空间是一个以初始化进程使用的文件系统为根节点的文件系统树。但是当clone系统调用使用CLONE_NEW创建新进程时,新近成拥有自己的命名空间。当进程挂载器卸载一个文件系统时,它只修改自己的命名空间,该过程对所有共享命名空间的进程是可见的。进程也可以通过privot_root系统调用修改命名空间的根文件系统。
Namespace用结构体namespace描述
namespace | ||
Type | Field | Description |
atomic_t | count | Usage counter (how many processes share the namespace) |
struct vfsmount * | root | Mounted filesystem descriptor for the root directory of the namespace |
struct list_head | list | Head of list of all mounted filesystem descriptors |
struct rw_semaphore | sem | Read/write semaphore protecting this structure |
一 加载文件系统
Ø 一个文件系统可能被多次加载,如果一个文件系统被n次加载,那么根文件系统可以通过n个加载点访问。尽管文件系统被加载n次,但是不管被加载多少次,超级块只有一个。
Ø 加载文件系统时层次进行的,第一个文件系统可能是第二个文件系统的目录。
Ø 同一个挂载点辛加载的文件系统会隐藏当前的文件系统,尽管进程已经使用旧文件系统的文件和目录。
Ø 如果顶端的挂载被删除,其他低端挂载可以被重新看见。
1. 挂载文件系统地描述符:vfsmount
vfsmount | ||
Type | Field | Description |
struct list_head | mnt_hash | Pointers for the hash table list. |
struct vfsmount * | mnt_parent | Points to the parent filesystem on which this filesystem is mounted. |
struct dentry * | mnt_mountpoint | Points to the dentry of the mount point directory where the filesystem is mounted. |
struct dentry * | mnt_root | Points to the dentry of the root directory of this filesystem. |
struct super_block * | mnt_sb | Points to the superblock object of this filesystem. |
struct list_head | mnt_mounts | Head of a list including all filesystem descriptors mounted on directories of this filesystem. |
struct list_head | mnt_child | Pointers for the mnt_mounts list of mounted filesystem descriptors. |
atomic_t | mnt_count | Usage counter (increased to forbid filesystem unmounting). |
int | mnt_flags | Flags. |
int | mnt_expiry_mark | Flag set to true if the filesystem is marked as expired (the filesystem can be automatically unmounted if the flag is set and no one is using it). |
char * | mnt_devname | Device filename. |
struct list_head | mnt_list | Pointers for namespace's list of mounted filesystem descriptors. |
struct list_head | mnt_fslink | Pointers for the filesystem-specific expire list. |
struct namespace * | mnt_namespace | Pointer to the namespace of the process that mounted the filesystem. |
Vfsmount常在下面的链表中出现:
Ø 哈西表:哈西表通过父文件系统的vfsmount和挂载点目录的dentry对象的地址索引。哈西表保存在mount_hashtable数组中,数组的大小和系统的内存有关。哈西表中的每个项是双向循环链表的头,该链表中保存所有具有相同哈西值的vfsmount描述符,mnt_hash保存链表中临近元素的指针。
Ø 命名空间:双向循环链表保存属于该命名空间中所有加载文件系统地描述符。Namespace的list域保存链表的头节点,而vfsmount中的mnt_list保存临近元素的指针。
Ø 加载的文件系统:双向循环链表中包括所有的子文件系统。链表的头保存在mnt_mounts中,但是mnt_child保存临近元素的指针。
2. 处理加载文件系统描述符的函数
alloc_vfsmnt(name):分配和初始化加载文件系统描述符。
free_vfsmnt(mnt):释放mnt 指向的加载文件系统描述符。
lookup_mnt(mnt, dentry):在哈西表中查找一个描述符,并且返回他的地址。
Mount调用sys_mount.
1. sys_mount得执行步骤为(fs/namespace.c):
(1) 复制参数到临时内核缓存。
retval = copy_mount_options (type, &type_page);
if (retval < 0)
return retval;
dir_page = getname(dir_name);
retval = PTR_ERR(dir_page);
if (IS_ERR(dir_page))
goto out1;
retval = copy_mount_options (dev_name, &dev_page);
if (retval < 0)
goto out2;
retval = copy_mount_options (data, &data_page);
if (retval < 0)
goto out3;
(2) 获取大内核锁
lock_kernel();
(3) 调用do_mount.
retval = do_mount((char*)dev_page, dir_page, (char*)type_page,
flags, (void*)data_page);
(4) 释放大内核锁,并且释放临时内核缓存。
unlock_kernel();
free_page(data_page);
2. do_mount的过程(fs/namespace.c)
(1) 如果设置MS_NOSUID,MS_NODEV或者MS_NOEXEC,清除设置,并且设置加载文件系统对象相应的标示。
/* Separate the per-mountpoint flags */
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
if (flags & MS_NODEV)
mnt_flags |= MNT_NODEV;
if (flags & MS_NOEXEC)
mnt_flags |= MNT_NOEXEC;
flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_ACTIVE);
(2) 调用path_lookup查找加载点的路径,结果保存在nameidata定义的变量nd中。
/* ... and get the mountpoint */
retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);
if (retval)
return retval;
(3) 检查加载标志
Ø 如果MS_REMOUNT设置,改变超级块对象中的s_flags和加载文件系统描述符中的mnt_flags,do_remount完成这些功能。
Ø 如果MS_BIND设置,用户请求使系统目录树的其他挂载点上的文件或者目录可见。do_loopback完成该功能。
* the work
*/
if (sys_access((const char __user *) "/init", 0) == 0)
execute_command = "/init";
else
prepare_namespace();
(2) prepare_namespace实现的过程(fs/do_mount.c)
====