Linux虚拟文件系统(3)– VFS系统调用

索引

Linux虚拟文件系统(1)

Linux虚拟文件系统(2)– 初始化流程

前言

前2章分别介绍了VFS的基本数据结构和初始化流程。本章介绍VFS文件系统的使用。文件系统使用大多是从应用层系统调用开始的,下表是对文件系统系统调用的一个整理。

Linux虚拟文件系统(3)-- VFS系统调用

MOUNT

VFS文件系统可以使用的前提,是系统挂载(Mount)成功。前一章中我们看到系统初始化过程中挂载了Rootfs,即根文件系统(/)。这里我们具体看一下rootfs挂载的细节。

static void __init init_mount_tree(void)
{
//......
  mnt = vfs_kern_mount(type, 0, "rootfs", NULL); //挂载rootfs,其中会执行对应rootfs的mount callback
  put_filesystem(type);
  if (IS_ERR(mnt))
    panic("Can't create rootfs");

  ns = create_mnt_ns(mnt);     //创建mnt命名空间,并将rootfs加入到对应命名空间,作为命名空间的root
//......
}

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
//.......
  mnt = alloc_vfsmnt(name);                         //分配VFS Mount
  if (!mnt)
    return ERR_PTR(-ENOMEM);

  if (flags & MS_KERNMOUNT)                         //如果是kern_mount,做内部Mount标记
    mnt->mnt.mnt_flags = MNT_INTERNAL;

  root = mount_fs(type, flags, name, data);        //真正执行fs的挂载
  if (IS_ERR(root)) {
    mnt_free_id(mnt);free_vfsmnt(mnt); return ERR_CAST(root); //如果Mount失败,就作清理
  }
  mnt->mnt.mnt_root = root;mnt->mnt.mnt_sb = root->d_sb;mnt->mnt_mountpoint = mnt->mnt.mnt_root;mnt->mnt_parent = mnt;  //挂载成功后,对vfsmount数据结构进行必要的赋值
  
  lock_mount_hash(); list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);   unlock_mount_hash(); //将新分配的VFS mount加入到root的mount列表
  return &mnt->mnt;
}

struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
  struct dentry *root;struct super_block *sb; 
        char *secdata = NULL;int error = -ENOMEM;
//...
  root = type->mount(type, flags, name, data); //执行mount函数进行mount,这里执行的是rootfs_mount
  if (IS_ERR(root)) {error = PTR_ERR(root); goto out_free_secdata; }
  sb = root->d_sb;  BUG_ON(!sb);  WARN_ON(!sb->s_bdi);
  sb->s_flags |= MS_BORN;
  
   error = security_sb_kern_mount(sb, flags, secdata);
  if (error) goto out_sb;

//....
}

static struct dentry *rootfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data)
{
	static unsigned long once;
	void *fill = ramfs_fill_super;
//....
	if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) //当CONFIG_TMPFS打开,使用shmem相关函数进行挂载和超级块填充,否则使用ramfs相关函数进行挂载和超级快填充
		fill = shmem_fill_super;
	return mount_nodev(fs_type, flags, data, fill);
}

struct dentry *mount_nodev(struct file_system_type *fs_type,
	int flags, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	int error;
//根据filesystem 类别获取超级块
	struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);

	if (IS_ERR(s))
		return ERR_CAST(s);

	error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); //执行fill_super函数,即ramfs_fill_super或者shmem_fill_super
	if (error) {
		deactivate_locked_super(s);
		return ERR_PTR(error);
	}
	s->s_flags |= MS_ACTIVE;
	return dget(s->s_root);
}
int shmem_fill_super(struct super_block *sb, void *data, int silent)
{
	struct inode *inode;
	struct shmem_sb_info *sbinfo;
	int err = -ENOMEM;

	/* Round up to L1_CACHE_BYTES to resist false sharing */
	sbinfo = kzalloc(max((int)sizeof(struct shmem_sb_info),
				L1_CACHE_BYTES), GFP_KERNEL);
	if (!sbinfo)
		return -ENOMEM;

	sbinfo->mode = S_IRWXUGO | S_ISVTX;
	sbinfo->uid = current_fsuid();
	sbinfo->gid = current_fsgid();
	sb->s_fs_info = sbinfo;
//进行必要的超级块初始化
#ifdef CONFIG_TMPFS
	if (!(sb->s_flags & MS_KERNMOUNT)) {
		sbinfo->max_blocks = shmem_default_max_blocks();
		sbinfo->max_inodes = shmem_default_max_inodes();
		if (shmem_parse_options(data, sbinfo, false)) {
			err = -EINVAL;
			goto failed;
		}
	} else {
		sb->s_flags |= MS_NOUSER;
	}
	sb->s_export_op = &shmem_export_ops;
	sb->s_flags |= MS_NOSEC;
#else
	sb->s_flags |= MS_NOUSER;
#endif

	spin_lock_init(&sbinfo->stat_lock);
	if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL))
		goto failed;
	sbinfo->free_inodes = sbinfo->max_inodes;

	sb->s_maxbytes = MAX_LFS_FILESIZE;
	sb->s_blocksize = PAGE_CACHE_SIZE;
	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
	sb->s_magic = TMPFS_MAGIC;
	sb->s_op = &shmem_ops;
	sb->s_time_gran = 1;
#ifdef CONFIG_TMPFS_XATTR
	sb->s_xattr = shmem_xattr_handlers;
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
	sb->s_flags |= MS_POSIXACL;
#endif
//从shmem初始化的cache中获取inode(作为/的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;
//设定超级块的root dentry
	sb->s_root = d_make_root(inode);
	if (!sb->s_root)
		goto failed;
	return 0;

failed:
	shmem_put_super(sb);
	return err;
}
shmem_init()
    |
    +-->shmem_init_inodecache()
    |
    +-->register_filesystem(&shmem_fs_type)
    |
    +-->kern_mount(&shmem_fs_type)
         |
         +----->vfs_kern_mount()
                  |
                  +--->alloc_vfsmnt(name)
                  |
                  +--->mount_fs(type, flags, name, data);
                          |
                          +---->type->mount(type, flags, name, data)
                                    |
                                    v
                               shmem_mount()

 

相关文章

Linux中断学习笔记(1) 什么是中断 CPU获取外设状态变化有两种方式: Polling:不断跟外设询问它的状态 当外设状态变化后主动通知CPU CPU要负责处理系统中各种各样的业务,如果频繁地轮询外设状态,必然会对整个系统的吞吐量产生影响,影响操作系统的正常运作。 中断便是外设通知CPU其状态变化...
Linux虚拟文件系统(2)– 初始化流程... 索引 http://l2h.site/linux-vfs-1/ http://l2h.site/linux-vfs-3/ 上文介绍了Linux VFS的基本架构与数据结构,本文介绍Linux虚拟文件系统的初始化过程。 如下图 +-----> start_kernel() |...
Linux虚拟文件系统(1) 索引 http://l2h.site/linux-vfs-2/ http://l2h.site/linux-vfs-3/ VFS简介 Linux将系统中很多资源都抽象成文件,如Socket、设备节点、以及内存。可以如此做,归功于Linux操作系统的虚拟文件系统(VFS)。 有了VFS,...
Linux中断学习笔记(2) — 嵌入式设备中断... 在Linux中断学习笔记(1)提到,外设通过中断控制器连接到CPU的中断线。嵌入式系统也不例外。 ARM嵌入式系统GIC架构 ARM官网所举图为例:ARM的中断控制器GIC(General Interrupt Controller)将从外设输入的中断通过CPU的IRQ信号线(ARM中主要为FIQ...

“Linux虚拟文件系统(3)– VFS系统调用”的5个回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注

− 5 = 4