Linux:关于 mount 的一些细节
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. mount 的主要细节
mount 从系统调用 sys_mount() 发起,如 mount -t tmpfs cgroup /sys/fs/cgroup:
sys_mount() /* fs/namespace.c */
do_mount()
do_new_mount()
struct vfsmount *mnt;
...
type = get_fs_type(fstype); /* 查找 @fstype 字串对应的文件系统类型 */
...
mnt = vfs_kern_mount(type, sb_flags, name, data);
struct mount *mnt;
struct dentry *root; /* @type 文件系统 的 根目录 */
...
mnt = alloc_vfsmnt(name);
...
root = mount_fs(type, flags, name, data);
struct dentry *root; /* @type 文件系统 的 根目录 */
struct super_block *sb; /* @type 文件系统 super block */
...
root = type->mount(type, flags, name, data);
shmem_mount() /* tmpfs 的 挂载接口 */
...
sb = root->d_sb;
...
/* mm/shmem.c */
static struct dentry *shmem_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_nodev(fs_type, flags, data, shmem_fill_super);
}
struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
...
/* 分配 super block 对象 */
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
...
error = fill_super(s, data, flags & SB_SILENT ? 1 : 0); /* shmem_fill_super(), ... */
...
}
int shmem_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode; /* tmpfs 根目录的 inode */
struct shmem_sb_info *sbinfo; /* 特定于 tmpfs 的 super block 信息 */
...
/* Round up to L1_CACHE_BYTES to resist false sharing */
sbinfo = kzalloc(max((int)sizeof(struct shmem_sb_info),
L1_CACHE_BYTES), GFP_KERNEL);
...
sb->s_fs_info = sbinfo;
...
sb->s_magic = TMPFS_MAGIC;
sb->s_op = &shmem_ops;
...
/* 创建 tmpfs 根目录的 inode */
inode = shmem_get_inode(sb, NULL, S_IFDIR | sbinfo->mode, 0, VM_NORESERVE);
if (!inode)
goto failed;
inode->i_uid = sbinfo->uid;
inode->i_gid = sbinfo->gid;
/* 创建 tmpfs 根目录对象 */
sb->s_root = d_make_root(inode);
...
}
static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
umode_t mode, dev_t dev, unsigned long flags)
{
struct inode *inode;
...
inode = new_inode(sb);
if (inode) {
inode->i_ino = get_next_ino(); /* 分配 inode 编号 */
...
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_generation = get_seconds();
...
/* 根据 inode 的类型,设置对应的接口 */
switch (mode & S_IFMT) {
...
case S_IFDIR: /* 这里的上下文是为 tmpfs 建立的 根目录 的 inode */
inc_nlink(inode);
/* Some things misbehave if size == 0 on a directory */
inode->i_size = 2 * BOGO_DIRENT_SIZE;
inode->i_op = &shmem_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
break;
...
}
} else
...
return inode;
}
看下来,mount 的重点工作是:
1. 建立文件系统的 super block 。
2. 建立根目录,同时绑定根目录的接口。
在例子中的上下文,是绑定 shmem_dir_inode_operations、simple_dir_operations
接口。
建立文件系统 super block,以及文件系统根目录和其接口设定,自然都是为了管理文件系统。super block 包含文件系统的很多信息,譬如关联的设备,最大 block 尺寸,等等,这里不详细展开。而文件系统根目录,首先是建立了文件系统起始路径,再就是根目录接口的设定,其主要目的是用来创建目录和文件。先看文件创建的过程:
sys_open()
do_sys_open()
do_filp_open()
path_openat()
struct file *file;
...
file = get_empty_filp(); /* 新建 file 对象 */
...
do_last(nd, file, op, &opened)
...
/* 创建文件 inode,设置文件 file 对象 */
lookup_open(nd, &path, file, op, got_write, opened)
...
/* Negative dentry, just create the file */
if (!dentry->d_inode && (open_flag & O_CREAT)) {
error = dir_inode->i_op->create(dir_inode, dentry, mode, open_flag & O_EXCL);
shmem_create() /* 创建文件 inode 和 接口 */
}
...
vfs_open(&nd->path, file, current_cred());
do_dentry_open(file, d_backing_inode(dentry), NULL, cred)
...
f->f_inode = inode; /* 绑定文件 file 的 inode */
...
/* 绑定文件 file 对象的接口 &simple_dir_operations */
f->f_op = fops_get(inode->i_fop);
...
static int shmem_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl)
{
return shmem_mknod(dir, dentry, mode | S_IFREG, 0);
}
static int
shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
struct inode *inode;
...
inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
if (inode) {
...
d_instantiate(dentry, inode);
...
}
return error;
...
}
static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
umode_t mode, dev_t dev, unsigned long flags)
{
struct inode *inode;
...
inode = new_inode(sb);
if (inode) {
inode->i_ino = get_next_ino(); /* 分配 inode 编号 */
...
switch (mode & S_IFMT) {
...
case S_IFREG: /* 文件类型 inode 接口设定 */
inode->i_mapping->a_ops = &shmem_aops;
inode->i_op = &shmem_inode_operations;
inode->i_fop = &shmem_file_operations;
mpol_shared_policy_init(&info->policy,
shmem_get_sbmpol(sbinfo));
break;
...
}
}
...
}
再看目录创建的过程:
sys_mkdir()
sys_mkdirat()
vfs_mkdir(path.dentry->d_inode, dentry, mode)
dir->i_op->mkdir(dir, dentry, mode)
shmem_mkdir()
shmem_mknod(dir, dentry, mode | S_IFDIR, 0)
struct inode *inode;
...
/* 创建 目录 的 inode ,过程参考前面 tmpfs 的 挂载过程 */
inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
if (inode) {
...
dir->i_size += BOGO_DIRENT_SIZE;
dir->i_ctime = dir->i_mtime = current_time(dir);
d_instantiate(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
}
到此,mount 的主要过程及相关细节已经分析完毕。
简单小结一下,mount 的主要目的就是 建立 super block 和 根目录,然后绑定的根目录接口又可以进行目录和文件的创建和接口绑定,目录下的目录的接口又可以创建目录和文件,如此层层递推。

浙公网安备 33010602011771号