目录
概述
Linux世界中一切都是文件,Linux文件子系统VFS(Virtual Filesystem)为用户提供了文件和文件系统的相关接口,系统中所有文件系统依赖VFS来协同工作。Linux的 文件包含两部分目录和文件,一般采用树状的结构进行组织,如:
│ ├── Desktop
│ │ └── JProfiler.desktop
│ ├── develop
│ │ ├── busybox
│ │ │ ├── applets
│ │ │ │ ├── applets.c
│ │ │ │ ├── applets.o
│ │ │ │ ├── applet_tables
│ │ │ │ ├── applet_tables.c
│ │ │ │ ├── built-in.o
路径字符串:
/home/dw/readme.txt
表示一个名称为readme.txt 的文件,它在目录/home/dw中。在这里目录和文件看起来属于不同的实例,但首先明确一下,在linux中目录也属于文件,它可以列出其中所包含的所有文件,它也可以执行文件操作。
文件的描述
文件的组成:
1. 文件
1. 文件的相关信息(创建时间,拥有者等也称为元数据inode)
2. 文件本身
2. 文件系统的控制信息
VFS为了描述为了描述文件,在VFS系统中使用了对应的四种对象:
• 超级块对象
• 索引节点对象
• 目录对象
• 文件对象
其中在大多linux系统中超级块对象会保存在磁盘的特定扇区否则会在使用现场创建
VFS系统的数据类型
数据类型 | 描述 |
---|---|
struct file_system_type | 描述文件系统类型,如ext4 |
struct super_block | 每种文件系统都必须实现超级块对象,用于存储特性文件系统信息,对应存放与磁盘特定扇区 |
struct inode | 包含内核操作的全部信息,对于linux可以从磁盘索引节点直接读入,否则要文件系统提取这些信息,在内存中创建,便于文件系统使用。 |
struct dentry | 文件包括路径中的每个部分都是目录项,没有对应的磁盘数据结构。有三种状态:1)被使用2)未被使用3)无效目录项 |
struct mount | 安装文件系统的实例,代表一个安装点,包含一个 struct vfsmount成员 |
struct file | 使用进程的观点看文件,表示进程已经打开的文件。包含访问模式,当前偏移等。 |
struct fs_struct | 进程描述符中使用,文件系统和进程相关信息,如当前工作目录和根目录 |
struct files_struct | 进程描述符中使用,进程的相关信息,如打开的文件以及描述符 |
struct nsproxy | 进程描述符中使用,进程的命名空间层次结构。默认情况下,所有的进程共享相同的命名空间,可以通过CLONE_NEWNS配置。 int copy_namespaces(unsigned long flags, struct task_struct *tsk) { ……………….. if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET)))) { get_nsproxy(old_ns); return 0; } ……………….. } |
各个数据结构之间的关系
各个数据结构之间的引用关系:
VFS初始化
在start_kernel中通過下面兩個函數初始化:
asmlinkage __visible void __init start_kernel(void)
{
…….
vfs_caches_init_early();
…….
vfs_caches_init(totalram_pages);
…….
}
其中:
void __init vfs_caches_init_early(void)
{
dcache_init_early(); //创建2个表 1:slab cach和2:一个hash表
inode_init_early(); //创建2个表 1:slab cach和2:一个hash表
}
void __init vfs_caches_init(unsigned long mempages)
{
unsigned long reserve;
/* Base hash sizes on available memory, with a reserve equal to
150% of current kernel size */
reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);
mempages -= reserve;
names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
dcache_init();
inode_init();
files_init(mempages); /*创建struct file的slab cache*/
//至此,创建了struct dentry, struct inode的slab cache和hash table,struct file 的slab cache,一共5个cache。
mnt_init();
bdev_cache_init();
chrdev_init();
}
在dcache_init,inode_init同dcache_init_early, inode_init_early函数中分别创建dentry和inode的slab cache和hash table,在slab cache中保存数据,使用hash table为其建立索引表,典型的以空间换时间方式。这里使用有early后缀和没有early后缀的函数是根据hash是否分布在NUMA上来选择hash table的创建时机是否推迟到vmalloc空间可以使用。
void __init inode_init(void)
{
unsigned int loop;
/* 创建inode slab cache */
inode_cachep = kmem_cache_create("inode_cache",
sizeof(struct inode),
0,
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
SLAB_MEM_SPREAD),
init_once);
........
/* 创建inode hash table*/
inode_hashtable =
alloc_large_system_hash("Inode-cache",
sizeof(struct hlist_head),
ihash_entries,
14,
0,
&i_hash_shift,
&i_hash_mask,
0,
0);
for (loop = 0; loop < (1U << i_hash_shift); loop++)
INIT_HLIST_HEAD(&inode_hashtable[loop]);
}
在调用sysfs_init函数之前为dentry, inode,file,mount,kernfs_node创建了slab cache,为了方便索引、提高效率还创建了部分hash表。
void __init mnt_init(void)
{
unsigned u;
int err;
/*创建struct mount 的slab cache,通过alloc_vfsmnt函数进行分配*/
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
mount_hashtable = alloc_large_system_hash("Mount-cache",
sizeof(struct hlist_head),
mhash_entries, 19,
0,
&m_hash_shift, &m_hash_mask, 0, 0);
mountpoint_hashtable = alloc_large_system_hash("Mountpoint-cache",
sizeof(struct hlist_head),
mphash_entries, 19,
0,
&mp_hash_shift, &mp_hash_mask, 0, 0);
if (!mount_hashtable || !mountpoint_hashtable)
panic("Failed to allocate mount hash table\n");
for (u = 0; u <= m_hash_mask; u++)
INIT_HLIST_HEAD(&mount_hashtable[u]);
for (u = 0; u <= mp_hash_mask; u++)
INIT_HLIST_HEAD(&mountpoint_hashtable[u]);
/*创建名为"kernfs_node_cache"的struct kernfs_node结构体的slab cache*/
kernfs_init();
err = sysfs_init(); //创建struct kernfs_root sysfs_root,注册sysfs文件系统
if (err)
printk(KERN_WARNING "%s: sysfs_init error: %d\n",
__func__, err);
fs_kobj = kobject_create_and_add("fs", NULL);
if (!fs_kobj)
printk(KERN_WARNING "%s: kobj create error\n", __func__);
//注册文件系统:rootfs和ramfs
init_rootfs();
init_mount_tree();
}
在上面函数中注册了3个文件系统类型(file_system_type),分别是:
sysfs : sysfs_fs_type
rootfs: rootfs_fs_type
ramfs : ramfs_fs_type
下面通过init_mount_tree 函数安装根文件系统 :其中,文件系统的安装和初始化需要分配super_block和inode,他们的分配初始化通过下面的调用完成。
init_mount_tree-->vfs_kern_mount-->
mount_fs-->rootfs_mount-->mount_nodev-->ramfs_fill_super。
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
static void __init init_mount_tree(void)
{
struct vfsmount *mnt;
struct mnt_namespace *ns;
struct path root;
struct file_system_type *type;
type = get_fs_type("rootfs"); //查找rootfs_fs_type
if (!type)
panic("Can't find rootfs type");
// 1). 分配struct mount 2).安装文件系统
mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
put_filesystem(type);
if (IS_ERR(mnt))
panic("Can't create rootfs");
// 创建一个私有的命名空间并添加一个根文件系统
ns = create_mnt_ns(mnt);
if (IS_ERR(ns))
panic("Can't allocate initial namespace");
init_task.nsproxy->mnt_ns = ns;
get_mnt_ns(ns);
root.mnt = mnt;
root.dentry = mnt->mnt_root;
mnt->mnt_flags |= MNT_LOCKED;
// 设置当前进程init_task的当前目录和根目录。
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
}
随后vfs_caches_init调用bdev_cache_init、chrdev_init。
void __init vfs_caches_init(unsigned long mempages)
{
......
mnt_init();
bdev_cache_init();
chrdev_init();
}
初始字符设备,块设备,完成VFS初始化。