linux虚拟文件系统

小前言

开写之前,插个题外话。内核相关内容写了几篇文章了,每篇文章篇幅都不少,特别是贴了不少代码,因为要看内核的具体实现,源码是必不可少的。为了方便阅读源码,添加了部分中文注释,也保留了原来的的英文注释,所以篇幅比较长。写这一段话,希望保持住啃内核的热情,不要轻易放弃。

进入正题。在linux操作系统当中,一切皆是文件,除了通所说狭义的文件(文本文件和二进制文件)以外,目录、套接字、设备及管道等都是文件。为了处理与文件系统相关的所有调用,表现为能够给各种文件系统提供一个通用的接口,使上层的应用程序能够使用通用的接口访问不同文件系统,同时也为不同文件系统的通信提供媒介,linux中设计了虚拟文件系统。

文件系统

先介绍不虚拟的文件系统。
文件系统在不同的上下文中有不同的含义:

  • 在存储设备缓存文件的方法,包括数据结构和访问方法。
  • 按照某种文件系统类型格式化的一块存储介质。
  • 内核中负责管理和存储文件的模块,即文件系统模块。

在linux中,可以通过命令mount -t fstype device dir把文件系统挂载到某个目录下,其中fstype是文件系统相关类型,device是挂载设备,dir是设备所对应的目录,这个命令底层是调用内核mount()函数。卸载挂载在某个目录上的文件系统,执行命令umount dir。这两个命令还可以有其他参数,这里略掉。关于文件系统的命令,还有open、close、write、read、lseek、fsync、fdatasync等。
fsync与fdatasync是磁盘同步命令。linux写文件时,内核的文件系统模块会把数据保存在缓存页中,不会立即写入存储设备,需要使用fsync命令把修改的文件属性和数据立即写入存储设备,或使用fdatasync把文件中修改的数据立即写入存储设备。
两者的区别,fdatasync函数类似于fsync,但它只影响文件的数据部分。而除数据外,fsync还会同步更新文件的属性。
再多说一点,内核中fwrite()函数也是同样的原理,可以使用fflush()冲刷流,把写到缓冲区的数据立即写到内核中。
linux文件系统的架构,分为用户空间、内核空间和硬件 3 个层面。看图
在这里插入图片描述
下面针对这三个层面,一个个介绍。

用户空间

应用程序可以直接使用内核提供的系统调用访问文件:

  • 一个存储设备上的文件系统,只有挂载到内存中目录树的某个目录下,进程才能访问这个文件系统。
  • 系统调用 umount 用来卸载某个目录下挂载的文件系统 。 可 以 执 行 命 令 umount dir 来 卸 载 文 件 系 统 ,umount 命令调用系统调用 umount()函数。

应用程序可以使用 glibc 库封装标准 I/O 流函数访问文件,标准 I/O 流提供缓冲区,目的是尽可能减少调用 read/write 次 数 , 提 高 性 能 。 标 准 I/O 流 函数:fopen/fclose/fread/fwrite(fflush)/fseek。

硬件层面

外部存储设备分为块设备、闪存和 NVDIMM(非易失性内存)设备 3 类。
块设备主要有 2 种类型:机械硬盘和闪存类块设备。机械硬盘读写单位为扇区,访问首先沿着半径备方向移动磁头寻找磁道,然后转动盘片找到扇区。闪存作为存储设备,里面的控制器运行固化驱动程序,驱动程序的功能是内存转换层,把闪存转换为块设备,对外表现为块设备。Solid state drives,SSD。手机/平板嵌入式存储卡 eMMC(embedded multi media card)/通用闪存存储 UFS(Universal flash storage)
这里再介绍一下DIMM(双列直插式存储模块)。设备把NAND闪存、内存、超级电容集成到一起,做到访问速度和内存一样快,且断电不丢失,因为瞬间断电时会有超级电容供电,内存中的数据会转移到NAND。
硬件不是重点,就介绍这么多。

内核空间

在内核的目录 fs 下可以看到,内核支持多种文件系统类型。为了对用户程序提供统一的文件操作接口,为了使不同的文件系统实现能够共享,内核实现一个抽象层,称为虚拟文件系统(Virtual File System,VFS),也称为虚拟文件系统切换(Virtual Filesystem Switch,VFS)。
文件系统分为:块设备文件系统(存储设备是机械硬盘和 SSD 等块,如EXT2/3/4)、闪存文件系统(存储设备 NOR 闪存、NAND 闪存,常用的闪存文件系统有目录型闪存文件系统2JFFS2、无序区块镜像文件系统UBIFS)、内存文件系统(文件在内存中,如tmpfs)、伪文件系统。
为什么需要针对闪存专门设计文件系统?一是因为软件需要识别并跳过NAND的存储坏块,二是因为需要实现损耗均匀,所有擦除块的擦除次数要做到均匀,避免一部分擦除块先损坏。
伪文件系统是为了使用vfs的编程接口而设计的假的文件系统,常用的有文件系统有:

  • proc:把内核的信息导出到用户空间。
  • sockfs:文件系统使用套接字,可以使用read()接收报文write()发送报文。
  • sysfs:把内核设备的所有信息导出到用户空间,挂载目录是/sys。
  • cgroup:控制组用来控制进程的资源,能让管理员可以写成文件的方式来配置。

虚拟文件系统数据结构

虽然不同文件系统类型的物理结构不同,但是虚拟文件系统定义一套统一的数据结构。下面介绍一些,又到了贴源码的环节……

超级块

文件系统的第一块是超级块,用来描述文件系统的总体信息。当我们把文件系统挂载到内存中目录树的一个目录下时,就会读取文件系统的超级块,在内存中创建超级块的副本。数据结构:

struct super_block {
    //用来把所有超级块实例链接到全局链表super_blocks
	struct list_head	s_list;		/* Keep this first */
    //保存文件系统所有的块设备,保存设备号
	dev_t			s_dev;		/* search index; _not_ kdev_t */
    //块长度,值为s_blocksize所占位数,即s_blocksize_bits=log2(s_blocksize)向上取整
	unsigned char		s_blocksize_bits;
    //块长度,单位字节。
	unsigned long		s_blocksize;
    //文件系统支持的最大长度
	loff_t			s_maxbytes;	/* Max file size */
    //指向文件系统类型
	struct file_system_type	*s_type;
    //指向超级块操作集合,下面会贴代码
	const struct super_operations	*s_op;
	const struct dquot_operations	*dq_op;
	const struct quotactl_ops	*s_qcop;
	const struct export_operations *s_export_op;
    //标志位
	unsigned long		s_flags;  
	unsigned long		s_iflags;	/* internal SB_I_* flags */
    //文件系统类型的魔幻数,每种文件系统类型分配一个唯一数
	unsigned long		s_magic;
    //指向根目录的结构体struct dentry
	struct dentry		*s_root;
	struct rw_semaphore	s_umount;
	int			s_count;
	atomic_t		s_active;
#ifdef CONFIG_SECURITY
	void                    *s_security;
#endif
	const struct xattr_handler **s_xattr;

	const struct fscrypt_operations	*s_cop;

	struct hlist_bl_head	s_anon;		/* anonymous dentries for (nfs) exporting */
	struct list_head	s_mounts;	/* list of mounts; _not_ for fs use */
	struct block_device	*s_bdev;
	struct backing_dev_info *s_bdi;
	struct mtd_info		*s_mtd;
	struct hlist_node	s_instances;
	unsigned int		s_quota_types;	/* Bitmask of supported quota types */
	struct quota_info	s_dquot;	/* Diskquota specific options */

	struct sb_writers	s_writers;

	char s_id[32];				/* Informational name */
	u8 s_uuid[16];				/* UUID */

	void 			*s_fs_info;	/* Filesystem private info */
	unsigned int		s_max_links;
	fmode_t			s_mode;

	/* Granularity of c/m/atime in ns.
	   Cannot be worse than a second */
	u32		   s_time_gran;

	/*
	 * The next field is for VFS *only*. No filesystems have any business
	 * even looking at it. You had been warned.
	 */
	struct mutex s_vfs_rename_mutex;	/* Kludge */

	/*
	 * Filesystem subtype.  If non-empty the filesystem type field
	 * in /proc/mounts will be "type.subtype"
	 */
	char *s_subtype;

	/*
	 * Saved mount options for lazy filesystems using
	 * generic_show_options()
	 */
	char __rcu *s_options;
	const struct dentry_operations *s_d_op; /* default d_op for dentries */

	/*
	 * Saved pool identifier for cleancache (-1 means none)
	 */
	int cleancache_poolid;

	struct shrinker s_shrink;	/* per-sb shrinker handle */

	/* Number of inodes with nlink == 0 but still referenced */
	atomic_long_t s_remove_count;

	/* Being remounted read-only */
	int s_readonly_remount;

	/* AIO completions deferred from interrupt context */
	struct workqueue_struct *s_dio_done_wq;
	struct hlist_head s_pins;

	/*
	 * Owning user namespace and default context in which to
	 * interpret filesystem uids, gids, quotas, device nodes,
	 * xattrs and security labels.
	 */
	struct user_namespace *s_user_ns;

	/*
	 * Keep the lru lists last in the structure so they always sit on their
	 * own individual cachelines.
	 */
	struct list_lru		s_dentry_lru ____cacheline_aligned_in_smp;
	struct list_lru		s_inode_lru ____cacheline_aligned_in_smp;
	struct rcu_head		rcu;
	struct work_struct	destroy_work;

	struct mutex		s_sync_lock;	/* sync serialisation lock */

	/*
	 * Indicates how deep in a filesystem stack this SB is
	 */
	int s_stack_depth;

	/* s_inode_list_lock protects s_inodes */
	spinlock_t		s_inode_list_lock ____cacheline_aligned_in_smp;
	struct list_head	s_inodes;	/* all inodes */

	spinlock_t		s_inode_wblist_lock;
	struct list_head	s_inodes_wb;	/* writeback inodes */
};

超级块操作集合

struct super_operations {
    //用来为一个索引节点分配内存并初始化
   	struct inode *(*alloc_inode)(struct super_block *sb);
    //用来释放内存中的索引节点
	void (*destroy_inode)(struct inode *);

    //用来把索引节点标记为脏
   	void (*dirty_inode) (struct inode *, int flags);
    //用来把一个索引节点写到存储设备
	int (*write_inode) (struct inode *, struct writeback_control *wbc);
    //用来在索引节点的引用计数减到0时调用
	int (*drop_inode) (struct inode *);
    //从存储设备上的文件系统删除一个索引节点
	void (*evict_inode) (struct inode *);
    //用来释放超级块
	void (*put_super) (struct super_block *);
	int (*sync_fs)(struct super_block *sb, int wait);
	int (*freeze_super) (struct super_block *);
	int (*freeze_fs) (struct super_block *);
	int (*thaw_super) (struct super_block *);
	int (*unfreeze_fs) (struct super_block *);
	int (*statfs) (struct dentry *, struct kstatfs *);
	int (*remount_fs) (struct super_block *, int *, char *);
	void (*umount_begin) (struct super_block *);

	int (*show_options)(struct seq_file *, struct dentry *);
	int (*show_devname)(struct seq_file *, struct dentry *);
	int (*show_path)(struct seq_file *, struct dentry *);
	int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
	ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
	ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
	struct dquot **(*get_dquots)(struct inode *);
#endif
	int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
	long (*nr_cached_objects)(struct super_block *,
				  struct shrink_control *);
	long (*free_cached_objects)(struct super_block *,
				    struct shrink_control *);
};

挂载描述符

一个文件系统,只有挂载到内存中目录树的一个目录下,进程才能访问这个文件系统。每次挂载文件系统,虚拟文件系统就会创建一个挂载描述符:mount结构体。挂载描述符用来描述文件系统的一个挂载实例,同一个存储设备上的文件系统可以多次挂载,每次挂载到不同的目录下。

/* 
为了方便分析,给一个案例。案例分析如下:
把文件系统2挂载到目录"/x"下,目录x属于文件系统1,目录x称为挂载点
文件系统2的mount实例是文件系统1的mount实例的孩子
文件系统1的mount实例是文件系统2的mount实例的父亲
这里假设下面是文件系统2的挂载描述符
*/
struct mount {
    //用来把挂载描述符加入全局散列表mnt_hashtable,关键字是(父挂载描述符,挂载点)
	struct hlist_node mnt_hash;
    //指向父亲,即文件系统1的mount实例
	struct mount *mnt_parent;
    //指向作为挂载点的目录,即文件系统1的目录x
	struct dentry *mnt_mountpoint;
    //文件系统2的挂载信息,下面贴代码
	struct vfsmount mnt;
	union {
		struct rcu_head mnt_rcu;
		struct llist_node mnt_llist;
	};
#ifdef CONFIG_SMP
	struct mnt_pcp __percpu *mnt_pcp;
#else
	int mnt_count;
	int mnt_writers;
#endif
    //孩子链表的头节点
	struct list_head mnt_mounts;	/* list of children, anchored here */
    //用来加入父亲的孩子链表
	struct list_head mnt_child;	/* and going through their mnt_child */
    //用来把挂载描述符添加到超级块的挂载实例链表中
    //同一个存储设备上的文件系统,可以多次挂载,每次挂载到不同的目录
	struct list_head mnt_instance;	/* mount instance on sb->s_mounts */
    //指向存储设备的名称,例如"/dev/dsk/hda1"
	const char *mnt_devname;	/* Name of device e.g. /dev/dsk/hda1 */
	struct list_head mnt_list;
	struct list_head mnt_expire;	/* link in fs-specific expiry list */
	struct list_head mnt_share;	/* circular list of shared mounts */
	struct list_head mnt_slave_list;/* list of slave mounts */
	struct list_head mnt_slave;	/* slave list entry */
	struct mount *mnt_master;	/* slave is on master->mnt_slave_list */
	struct mnt_namespace *mnt_ns;	/* containing namespace */
    //指向挂载点,下面贴代码
	struct mountpoint *mnt_mp;	/* where is it mounted */
	struct hlist_node mnt_mp_list;	/* list mounts with the same mountpoint */
	struct list_head mnt_umounting; /* list entry for umount propagation */
#ifdef CONFIG_FSNOTIFY
	struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
	__u32 mnt_fsnotify_mask;
#endif
	int mnt_id;			/* mount identifier */
	int mnt_group_id;		/* peer group identifier */
	int mnt_expiry_mark;		/* true if marked for expiry */
	struct hlist_head mnt_pins;
	struct fs_pin mnt_umount;
	struct dentry *mnt_ex_mountpoint;
};

挂载信息

struct vfsmount {
    //指向文件系统2的根目录
    struct dentry *mnt_root; /* root of the mounted tree */
    //指向文件系统2的超级块
    struct super_block *mnt_sb; /* pointer to superblock */
    int mnt_flags;
};

挂载点

struct mountpoint {
struct hlist_node m_hash;
    //指向作为挂载点的目录
    struct dentry *m_dentry;
    //用来把同一个挂载点下的所有挂载描述符链接起来
    struct hlist_head m_list;
    int m_count;
};

文件系统类型

因为每种文件系统类型的超级块的格式不同,所以每种文件系统需要向虚拟文件系统注册文件系统类型 file_system_type,并且实现 mount 方法用来读取和解析超级块。

struct file_system_type {
    //文件系统类型的名称
	const char *name;
	int fs_flags;
#define FS_REQUIRES_DEV		1 
#define FS_BINARY_MOUNTDATA	2
#define FS_HAS_SUBTYPE		4
#define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
#define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
    //用来在挂载文件系统的时候读取并解析超级块
	struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *);
    //用来在卸载文件系统的时候释放超级块
	void (*kill_sb) (struct super_block *);
    //指向实现文件系统的模块的指针
	struct module *owner;
    //指向文件系统类型链表中下一个元素的指针
	struct file_system_type * next;
    //多个存储设备上的文件系统的类型可能相同,此成员用来把相同文件系统类型的超级块链接起来
	struct hlist_head fs_supers;

	struct lock_class_key s_lock_key;
	struct lock_class_key s_umount_key;
	struct lock_class_key s_vfs_rename_key;
	struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];

	struct lock_class_key i_lock_key;
	struct lock_class_key i_mutex_key;
	struct lock_class_key i_mutex_dir_key;
};

索引节点

在文件系统中,每个文件对应一个索引节点,索引节点描述两类信息。

  • 文件的属性,也称为元数据(metadata)。
  • 文件数据的存储位置,每个索引节点有一个唯一的编号。当内核访问存储设备上的一个文件时,会在内存中创建索引节点的一个副本。

看代码

struct inode {
    //文件类型和访问权限
	umode_t			i_mode;
	unsigned short		i_opflags;
    //创建文件的用户的标识符
	kuid_t			i_uid;
    //创建文件的用户所属的组标识符
	kgid_t			i_gid;
	unsigned int		i_flags;

#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl	*i_acl;
	struct posix_acl	*i_default_acl;
#endif

	const struct inode_operations	*i_op;
    //指向文件所属的文件系统的超级块
	struct super_block	*i_sb;
    //指向文件的地址空间
	struct address_space	*i_mapping;

#ifdef CONFIG_SECURITY
	void			*i_security;
#endif

	/* Stat data, not accessed from path walking */
	unsigned long		i_ino;  //索引节点编号
	/*
	 * Filesystems may only read i_nlink directly.  They shall use the
	 * following functions for modification:
	 *
	 *    (set|clear|inc|drop)_nlink
	 *    inode_(inc|dec)_link_count
	 */
	union {
		const unsigned int i_nlink;  //硬链接计数
		unsigned int __i_nlink;
	};
	dev_t			i_rdev;  //设备号
	loff_t			i_size;  //文件长度
    //下三者区分在后面介绍
	struct timespec		i_atime;  //上一次访问文件的时间
	struct timespec		i_mtime;  //上一次修改文件数据的时间
	struct timespec		i_ctime;  // 上一次修改文件节点的时间

	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	unsigned short          i_bytes;  //文件长度除以块长度的余数
    //inode块长度位数,即长度是 2 的 i_blkbits 次幂字节
    unsigned int		i_blkbits;  	
    blkcnt_t		i_blocks;  //文件的块数

#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif

	/* Misc */
	unsigned long		i_state;
	struct rw_semaphore	i_rwsem;

	unsigned long		dirtied_when;	/* jiffies of first dirtying */
	unsigned long		dirtied_time_when;

	struct hlist_node	i_hash;
	struct list_head	i_io_list;	/* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
	struct bdi_writeback	*i_wb;		/* the associated cgroup wb */

	/* foreign inode detection, see wbc_detach_inode() */
	int			i_wb_frn_winner;
	u16			i_wb_frn_avg_time;
	u16			i_wb_frn_history;
#endif
	struct list_head	i_lru;		/* inode LRU list */
	struct list_head	i_sb_list;
	struct list_head	i_wb_list;	/* backing dev writeback list */
	union {
		struct hlist_head	i_dentry;
		struct rcu_head		i_rcu;
	};
	u64			i_version;
	atomic_t		i_count;
	atomic_t		i_dio_count;
	atomic_t		i_writecount;
#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct file_lock_context	*i_flctx;
	struct address_space	i_data;
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;  //指向块设备
		struct cdev		*i_cdev;  //指向字符设备
		char			*i_link;
		unsigned		i_dir_seq;
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct fsnotify_mark_connector __rcu	*i_fsnotify_marks;
#endif

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
	struct fscrypt_info	*i_crypt_info;
#endif

	void			*i_private; /* fs or device private pointer */
};

区分一下三个时间。

  • i_atime:文件最后访问时间,即是文件最后的读取时间,例如:用命令cat filename,此时间修改,其他两个时间不修改。
  • i_ctime:结点最后修改时间,即是修改inode结构的时间,例如:用命令ln filename1 filename2,此时间修改,其他两个时间不修改。
  • i_mtime:文件最后修改时间,即是对文件内容的修改时间,例如:用命令echo aaa >filename,以上三个时间都修改。

目录项

文件系统把目录当作文件,这种文件的数据是由目录项结构组成的,每个目录项存储一个子目录或文件的名称以及对应的索引节点号。当内核访问存储设备上的一个目录项时,会在内核中创建目录项的一个副本。

struct dentry {
	/* RCU lookup touched fields */
	unsigned int d_flags;		/* protected by d_lock */
	seqcount_t d_seq;		/* per dentry seqlock */
    //用来把目录项加入散列表
	struct hlist_bl_node d_hash;	/* lookup hash list */
    //指向父目录
	struct dentry *d_parent;	/* parent directory */
    //存储文件名称
	struct qstr d_name;
    //指向对应的文件节点
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative */
	unsigned char d_iname[DNAME_INLINE_LEN];	/* small names */

	/* Ref lookup also touches following */
	struct lockref d_lockref;	/* per-dentry lock and refcount */
	const struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	unsigned long d_time;		/* used by d_revalidate */
	void *d_fsdata;			/* fs-specific data */

	union {
		struct list_head d_lru;		/* LRU list */
		wait_queue_head_t *d_wait;	/* in-lookup ones only */
	};
	struct list_head d_child;	/* child of parent list */
	struct list_head d_subdirs;	/* our children */
	/*
	 * d_alias and d_rcu can share memory
	 */
	union {
		struct hlist_node d_alias;	/* inode alias list */
		struct hlist_bl_node d_in_lookup_hash;	/* only for in-lookup ones */
	 	struct rcu_head d_rcu;
	} d_u;
};

目录项与索引节点的对应关系
在这里插入图片描述

文件打开实例及打开文件表

当进程打开一个文件的时候,虚拟文件系统就会创建一个打开实例:file 结构体。

struct file {
	union {
		struct llist_node	fu_llist;
		struct rcu_head 	fu_rcuhead;
	} f_u;
    //存储文件在目录树中的位置,下面贴代码
	struct path		f_path;
    //指向文件的索引节点
	struct inode		*f_inode;	/* cached value */
    //指向文件操作命令
	const struct file_operations	*f_op;

	/*
	 * Protects f_ep_links, f_flags.
	 * Must not be taken from IRQ context.
	 */
	spinlock_t		f_lock;
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;  //访问模式
	struct mutex		f_pos_lock;
	loff_t			f_pos;  //文件偏移,进程当前正在访问的位置
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;

	u64			f_version;
#ifdef CONFIG_SECURITY
	void			*f_security;
#endif
	/* needed for tty driver, and maybe others */
	void			*private_data;

#ifdef CONFIG_EPOLL
	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct list_head	f_ep_links;
	struct list_head	f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;  //指向文件的地址空间
} __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */

struct file_handle {
	__u32 handle_bytes;
	int handle_type;
	/* file identifier */
	unsigned char f_handle[0];
};

解释一下为什么有private_data成员。由于挂载目录下可能会有多个文件,而read/write回调是针对文件系统的操作,如果对不同文件进行读写操作,那么这些不同的文件数据就无法区分。解决措施就是在inode成员里加入一个成员,因为inode是每个文件都有一个实例,所以这个成员可以实现私有空间的功能。
path结构体

struct path {
    struct vfsmount *mnt; // 指向文件所属文件系统的挂载描述符的成员 mnt
    struct dentry *dentry; // 文件对应的目录项
};

文件系统信息结构

struct fs_struct {
	int users;
	spinlock_t lock;
	seqcount_t seq;
	int umask;
	int in_exec;
    //root存储进程的根目录,pwd存储进程当前的工作目录
	struct path root, pwd;
};

打开文件表真正记录哪些文件描述符被使用了,哪些是空闲的,实际是一个文件描述符位图,每1bit表示了一个文件描述符。

struct fdtable {
	unsigned int max_fds;    /*fdtable能管理的打开文件最大数量,由位图大小决定*/
	struct file __rcu **fd;  /*指向file指针数组的指针*/
	unsigned long *close_on_exec;  /*执行execve()系统调用时关闭文件的位图*/
	unsigned long *open_fds;  /*进程打开文件位图*/
	unsigned long *full_fds_bits;
	struct rcu_head rcu;
};

这些数据结构之间的关系
在这里插入图片描述

注册文件系统类型

因为每种文件系统的超级块的格式不同,所以每种文件系统需要向虚拟文件系统注册文件系统类型file_system_type,实现 mount 方法来读取和解析超级块。
函数 register_filesystem 用来注册文件系统类型,看代码

/**
 *	register_filesystem - register a new filesystem
 *	@fs: the file system structure
 *
 *	Adds the file system passed to the list of file systems the kernel
 *	is aware of for mount and other syscalls. Returns 0 on success,
 *	or a negative errno code on an error.
 *
 *	The &struct file_system_type that is passed is linked into the kernel 
 *	structures and must not be freed until the file system has been
 *	unregistered.
 */
 
int register_filesystem(struct file_system_type * fs)
{
	int res = 0;
	struct file_system_type ** p;

	BUG_ON(strchr(fs->name, '.'));
	if (fs->next)
		return -EBUSY;
	write_lock(&file_systems_lock);
	p = find_filesystem(fs->name, strlen(fs->name));  //这里遍历的链表头是全局变量
	if (*p)
		res = -EBUSY;
	else
		*p = fs;
	write_unlock(&file_systems_lock);
	return res;
}

EXPORT_SYMBOL(register_filesystem);

可以执行命令cat /proc/filesystems查看已经注册的文件系统类型。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模拟Linux文件系统。在任一OS下,建立一个大文件,把它假象成一张盘,在其中实现一个简单的 模拟Linux文件系统 在现有机器硬盘上开辟20M的硬盘空间,作为设定的硬盘空间。 2. 编写一管理程序对此空间进行管理,以模拟Linux文件系统,具体要求如下: (1) 要求盘块大小1k 正规文件 (2) i 结点文件类型 目录文件 (共1byte) 块设备 管道文件 物理地址(索引表) 共有13个表项,每表项2byte 文件长度 4byte 。联结计数 1byte (3)0号块 超级块 栈长度50 空闲盘块的管理:成组链接 ( UNIX) 位示图法 (Linux) (4)每建一个目录,分配4个物理块 文件名 14byte (5)目录项信息 i 结点号 2byte (6)结构: 0#: 超级块 1#-20#号为 i 结点区 20#-30#号为根目录区 3. 该管理程序的功能要求如下: (1) 能够显示整个系统信息,源文件可以进行读写保护。目录名和文件名支持全路径名和相对路径名,路径名各分量间用“/”隔开。 (2) 改变目录:改变当前工作目录,目录不存在时给出出错信息。 (3) 显示目录:显示指定目录下或当前目录下的信息,包括文件名、物理地址、保护码、文件长度、子目录等(带/s参数的dir命令,显示所有子目录)。 (4) 创建目录:在指定路径或当前路径下创建指定目录。重名时给出错信息。 (5) 删除目录:删除指定目录下所有文件和子目录。要删目录不空时,要给出提示是否要删除。 (6) 建立文件(需给出文件名,文件长度)。 (7) 打开文件(显示文件所占的盘块)。 (8) 删除文件:删除指定文件,不存在时给出出错信息。 4. 程序的总体流程为: (1) 初始化文件目录; (2) 输出提示符,等待接受命令,分析键入的命令; (3) 对合法的命令,执行相应的处理程序,否则输出错误信息,继续等待新命令,直到键入EXIT退出为止。
1. 在现有机器硬盘上开辟100M的硬盘空间,作为设定的硬盘空间。 2. 编写一管理程序simdisk对此空间进行管理,以模拟Linux文件系统,要求: (1) 盘块大小1k (2) 空闲盘块的管理:Linux位图法 (3) 结构:超级块, i结点区, 根目录区 3. 该simdisk管理程序的功能要求如下: (1) info: 显示整个系统信息(参考Linux文件系统的系统信息),文件可以根据用户进行读写保护。目录名和文件名支持全路径名和相对路径名,路径名各分量间用“/”隔开。 (2) cd …: 改变目录:改变当前工作目录,目录不存在时给出出错信息。 (3) dir …: 显示目录:显示指定目录下或当前目录下的信息,包括文件名、物理地址、保护码、文件长度、子目录等(带/s参数的dir命令,显示所有子目录)。 (4) md …: 创建目录:在指定路径或当前路径下创建指定目录。重名时给出错信息。 (5) rd …: 删除目录:删除指定目录下所有文件和子目录。要删目录不空时,要给出提示是否要删除。 (6) newfile …: 建立文件。 (7) cat …: 打开文件。 (8) copy …: 拷贝文件,除支持模拟Linux文件系统内部的文件拷贝外,还支持host文件系统与模拟Linux文件系统间的文件拷贝,host文件系统的文件命名为<host>…,如:将windows下D:盘的文件\data\sample\test.txt文件拷贝到模拟Linux文件系统中的/test/data目录,windows下D:盘的当前目录为D:\data,则使用命令: simdisk copy <host>D:\data\sample\test.txt /test/data 或者:simdisk copy <host>D:sample\test.txt /test/data (9) del …: 删除文件:删除指定文件,不存在时给出出错信息。 (10) check: 检测并恢复文件系统:对文件系统中的数据一致性进行检测,并自动根据文件系统的结构和信息进行数据再整理。 4. 程序的总体流程为: (1) 初始化文件目录; (2) 输出提示符,等待接受命令,分析键入的命令; (3) 对合法的命令,执行相应的处理程序,否则输出错误信息,继续等待新命令,直到键入EXIT退出为止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值