龙空技术网

「技术干货」图解虚拟文件系,从VFS到内核系统

嵌入式Linux内核 439

前言:

目前大家对“mysqlcantrmdir”可能比较讲究,大家都需要分析一些“mysqlcantrmdir”的相关资讯。那么小编同时在网上汇集了一些有关“mysqlcantrmdir””的相关知识,希望大家能喜欢,咱们一起来学习一下吧!

最近一直忙于基于android的动态分区的方案和virtual A/B方案通过rust重写升级的框架,里面有将IO子系统的框架学习了,主要是基于deviceMapper的用法和实现原理,只是学习了基本的原理和如何使用,对于其最底下的IO层基本不知道如何实现,准备放到文件IO章节学习。本周末接着学习文件系统,之前了解了以下文件系统的基本知识,本次开始真正进入到内核的虚拟文件系统学习之旅。

VFS架构

我们从一个简单的问题开始,我们如何访问文件?作为一个普通的使用者,我们不太关系我们如何访问计算机上的数据,不管这个数据是存储在机械硬盘上,还是SSD,甚至是远程的设备上;其次,我们也不想关心,我们现在用的是基于windows的ntfs还是Linux的ext4,不关心我该如何去挂载。正是通过设计,linux的设计者需要把我们的大脑从思考数据存储在哪里以及如何存储这样的细节中解放出来,Linux设计了一个文件系统的中间层,上层用户都直接和VFS打交道,文件系统开发者再把VFS转换为自己的格式。这样做的优点主要有:

用户层应用不用关心具体用的是什么文件系统, 使用统一的标准接口进行文件操作,例如read,write如果一个系统包含不同分区,不同分区使用不同的文件系统,他们之间可以通过这个VFS交互,比如从U盘、网盘拷数据到硬盘就得通过VFS转换管理信息可以动态支持很多文件系统,添加一个只需要安装驱动就可以了,不需要内核重新编译。

嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧。

需要的朋友私信【内核】即可领取

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂

要先有个大视角,让我们站在万米高空,鸟瞰我们的虚拟文件系统。虚拟文件系统的主要功能,是实现多种文件系统操作接口的统一,既能让上层的调用者使用同一套接口与底层的各种文件系统交互,又能对文件系统提供一个标准接口,使Linux系统能同时支持多种文件系统。虚拟文件系统与上层应用以及底层的各种文件系统之间的关系如图1所示。

我们不能总是从高空去俯瞰虚拟文件系统,其中包括有很多细节,如下图所示

了解所有的细节会导致文档比较长,所以我们开始分解VFS并浏览所涉及的主要数据结构,VFS采用面向对象的设计,主要包括超级块、索引节点、目录项、文件四个结构体对象。

由之前的理论学习,我们知道:

超级块: 存储文件系统的控制信息。描述文件系统的状态、文件系统类型、大小、区块数、索引节点数等,存放于磁盘的特定扇区中。索引节点: 存储文件的元数据信息。描述文件的大小、拥有者、创建时间、访问模式、磁盘mapping等。目录: 就是一个文件,存放的数据就是目录信息,所以操作文件的接口完全可以用在目录上。目录项: 在每一个文件路径中,路径的每一部分都被称为目录项;如径/home/source/helloworld.c中,目录 home, source和文件 helloworld.c都是一个目录项。文件: 一组在逻辑上具有完整意义的信息项的系列。除了普通文件,其他诸如目录、设备、套接字等 也以文件被对待。文件系统类型

若要内核能够识别、挂载某一类型的文件系统,内核必须知道文件系统类型,定义在include/linux/fs.h 中

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;              //没有选择LOCKDEP配置选项为空结构体	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;};

file_system_type结构体内主要成员简介如下:

mount():文件系统类型定义的挂载函数,第一个参数为指向文件系统类型的指针,第二个参数为挂载标记,第三个参数为文件系统所在块设备文件名称字符串,第四个参数为文件系统私有数据指针。主要完成超级块super_block结构体、文件系统根目录项dentry和节点inode结构体实例的创建和初始化

fs_supers:散列链表头,链接内核挂载的同类型文件系统的超级块实例。内核可以挂载同类型的多个文件系统,例如:硬盘中有两个分区被格式化成ext2文件系统,将两个分区都挂载到内核根文件系统后,具有两个ext2文件系统的超级块实例,它们被链接到ext2文件系统类型实例的fs_supers链表中。

next: 指向下一个file_system_type实例,所有注册的file_system_type实例在内核中组成单链表。

内核中所有注册的文件系统类型file_system_type实例由单链表管理,表头为file_systems,定义在/fs/filesystem.c文件内,注册file_system_type实例就是将其插入到单链表。

static struct file_system_type *file_systems;static DEFINE_RWLOCK(file_systems_lock);

文件系统类型实例的操作函数简列如下(fs/filesystem.c):

register_filesystem(struct file_system_type * fs):向内核注册文件系统类型实例,函数只是简单地将file_system_type实例添加到file_systems单链表。unregister_filesystem(struct file_system_type * fs):将file_system_type实例从全局单链表中移出。struct file_system_type get_fs_type(const char name): 由文件系统类型名称字符串查找结构体实例。

各文件系统类型的实现代码位于/fs/目录下,每个类型对应一个子目录。例如:ext2文件系统类型的代码位于/fs/ext2/目录下。文件系统类型的初始化函数在内核初始化子系统或加载模块时调用,主要完成文件系统类型的注册,私数据结构的创建、初始化等工作

可以通过以下的命令,查看内核支持的文件系统类型

超级块

内核中每挂载文件系统需要创建超级块super_block结构体实例,super_block结构体表示挂载文件系统的整体信息,例如:文件系统类型、数据块的大小核数量等。

通常块设备(分区)中文件系统的开头分出一个指定大小的区域,用于保存此分区文件系统的信息,此区域对应虚拟文件系统中定义的超级块。在挂载文件系统时,内核从此区域中读取信息,填充至超级块super_block实例中,超级块在include/linux/fs.hs中定义

struct super_block {	struct list_head	s_list;		               //将实例链接到全局双链表super_blocks	dev_t			s_dev;		                       //所在块设备(分区)设备号   	unsigned char		s_blocksize_bits;          //数据块大小以2的底取对数    	unsigned long		s_blocksize;               //数据块大小,字节数	loff_t			s_maxbytes;	                   //最大文件长度,字节数	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		*s_root;                  //指向文件系统根目录项dentry实例	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;	       //挂载mount结构体实例链表,一个分区可以执行多个挂载操作	struct block_device	*s_bdev;       //块设备(分区)对应块设备数据结构指针	struct backing_dev_info *s_bdi;    //后备存储设备信息	struct mtd_info		*s_mtd;          //MTD设备信息	struct hlist_node	s_instances;     //散列表节点,链入文件系统类型散列链表,表头fs_supers	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;	            //具体文件系统私有数据指针	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;    //赋予所有目录项dentry实例d_op成员	/*	 * Saved pool identifier for cleancache (-1 means none)	 */	int cleancache_poolid;	struct shrinker s_shrink;	                //slab缓存收缩器,用于页面回收机制	/* 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;   //dentry实例LRU链表	struct list_lru		s_inode_lru ____cacheline_aligned_in_smp;    //inode实例LRU链表	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 */}

super_block结构体中主要成员简介如下:

s_list: 双链表成员,将实例链接到全局双链表super_blocks。s_blocksize,s_blocksize_bits: 表示分区(块设备)数据块的大小,即读写块设备的数据单位,s_blocksize表示数据块大小,字节数,s_blocksize_bits表示数据块大小字节数以2为底取对数,即2s_blocksize_bits=s_blocksize。在对块设备进行读写操作时需要此数据,数据块大小来源于块设备的请求队列,在挂载文件系统创建超级块实例之后,从请求队列中获取此数值。s_op: 超级块操作super_operations结构体指针s_flags:标记成员,取值定义在/include/uapi/linux/fs.h头文件内,表示整个文件系统的属性,比如我们常见的只读,忽略suid和sgid位,禁止访问设备文件等,主要是挂载的文件属性s_root: 指向挂载文件系统根目录dentry实例s_inodes: 双链表成员,链接文件系统打开文件的inode实例s_mounts: 双链表成员,链接挂载mount结构体实例,文件系统可以有多个挂载点,每个mount结构体实例表示一次挂载操作super_block结构体中包含两个虚拟文件系统的接口数据结构,dentry_operations和super_operations。super_operations结构体表示超级块操作结构,其中包含对整个文件系统的操作、控制函数指针,如:分配inode、销毁inode、同步文件(文件内容写入块设备)等。

struct super_operations {  struct inode *(*alloc_inode)(struct super_block *s  b);      //创建inode实例	void (*destroy_inode)(struct inode *);                       //销毁alloc_inode创建实例               void (*dirty_inode) (struct inode *, int flags);            //标记inode脏,表示inode需要回写	int (*write_inode) (struct inode *, struct writeback_control *wbc); //回写inode元数据	int (*drop_inode) (struct inode *);               //释放inode时调用                                          	void (*evict_inode) (struct inode *);             //释放inode调用	void (*put_super) (struct super_block *); //当虚拟文件系统要释放super_block时调用此函数        	int (*sync_fs)(struct super_block *sb, int wait); //同步文件系统中所有脏inode	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 //分别用于VFS读写文件系统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 *);};

每一个类型的文件系统,需要提供这个结构体,例如ext4文件系统

其数据结构的关系图如下图所示

全局super_block链表:这个链表包含了系统中的所有super_block(不止一种文件系统super_block)。通过一个list_head类型的全局变量super_blocks来访问,内核中使用宏实现这个全局变量的初始化:

static LIST_HEAD(super_blocks);static DEFINE_SPINLOCK(sb_lock);list_add_tail(&s->s_list, &super_blocks);	hlist_add_head(&s->s_instances, &type->fs_supers);

当有新的文件系统挂载时,它会通过自身super_block结构中list_head类型的成员s_list链接到super_blocks上,形成一个全局的链表

一个文件系统类型(如ext4),可以有多个super_block实例。举例来说,我的Linux系统中,划分了3个分区分别挂载在/,/home,/boot目录上,挂载的文件系统都是ext4,因此,这3个ext4文件系统实例都有自己的super_block,它们通过super_block中hlist_node类型的成员s_instances链接成“ext4的super_block链表”。可以通过file_system_type的fs_supers成员来访问:

索引节点

目录项中保存了普通目录、文件的名称等少量信息,主要用于文件的管理,构成文件树状层次结构。文件的具体信息,如读写权限、修改时间、文件内容等,保存在被称为节点的结构中。回顾下上一节介绍的目录结构,在实际的文件系统中,保存文件内容的数据块号等信息不是保存在目录项中,而是保存在被称为节点的结构中,如下图所示

内存中,每个文件都有一个inode,一切皆文件,其实应该是说一切皆inode。inode保存了文件系统中一个文件的属性描述,比如文件类型,文件权限,属主,文件创建、读取或修改的时间等等。除了内存中的inode,磁盘中也有对应的inode结构,比如ext4文件系统中,内存中的inode是struct ext4_inode_info,而磁盘中的inode是struct ext4_inode_info,今天我们主要分析下内存中的inode结构。

4.1 节点数据结构

对于所有文件系统对象类型,所有文件系统中的每个对象都存在一个 inode,即索引节点的缩写。它在include/linux/fs.h中定义

struct inode {	umode_t			i_mode;                        //文件类型及访问权限           	unsigned short		i_opflags;               //进程打开文件的标记	kuid_t			i_uid;                         //文件用户主ID  	kgid_t			i_gid;                         //文件用户组ID	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;          //文件地址空间结构体指针,通常指向i_data成员,指向inode在内存中的pagecache#ifdef CONFIG_SECURITY	void			*i_security;#endif	/* Stat data, not accessed from path walking */	unsigned long		i_ino;                    //inode编号,inode节点号 ls -i 可以查看文件inode号	/*	 * 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;             //最后修改inode时间	spinlock_t		i_lock;	                 //锁定i_blocks, i_bytes, maybe i_size	unsigned short          i_bytes;       //块大小,字节数	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;	 //inode变脏的时间	unsigned long		dirtied_time_when;  //通过该变量链接到全局inode哈希表inode_hashtable,用于inode的快速查找	struct hlist_node	i_hash;              //将实例链入全局散列表	struct list_head	i_io_list;	         //将实例链入后备存储设备回写inode链表#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;		     //将实例链入超级块LRU链表,表头sb.s_inode_lru	struct list_head	i_sb_list;     //将实例链入超级块inode链表,表头sb.s_inodes	struct list_head	i_wb_list;	/* backing dev writeback list */	union {		struct hlist_head	i_dentry;     //散列链表头,链接关联的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;	  //文件操作结构指针,文件操作的接口	struct file_lock_context	*i_flctx;      //文件地址空间实例,缓存文件内容(页缓存)	struct address_space	i_data;	struct list_head	i_devices;   //inode表示设备文件时,将实例链接到cdev.list或block_device.bd_inodes链表	union {		struct pipe_inode_info	*i_pipe;   //管道文件		struct block_device	*i_bdev;       //指向块设备结构体 block_device		struct cdev		*i_cdev;             //指向字符设备结构体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 hlist_head	i_fsnotify_marks;#endif#if IS_ENABLED(CONFIG_FS_ENCRYPTION)	struct fscrypt_info	*i_crypt_info;#endif	void			*i_private;    //指向具体文件系统私有数据}

inode结构体主要成员成员简介如下:

i_mode:标记文件类型及访问权限。数据类型为umode_t,它是一个16位的无符号整型数,定义在/include/linux/types.h头文件内。各标记位定义如下:i_mapping: 文件地址空间address_space结构体指针成员,通常指向i_data成员i_data: 文件地址空间address_space结构体实例,主要用于在基于块设备的文件系统中管理文件系统内容的页缓存和实现文件缓存页数据与块设备的同步。i_op: inode_operations结构体指针,此结构主要用于对目录文件内容的操作,目录项的操作,如查找目录项,创建目录项,删去目录项,文件重命名等i_fop: file_operations结构体,此结构主要用于对文件内容的操作,如读写文件内容等

inode_operations结构体主要包含对目录文件内容操作的函数指针。目录文件的内容是目录项,因此对目录文件内容的操作就是对目录项的操作,例如:在目录下查找指定名称的子目录项、创建新目录项(新文件)、删除目录项、文件重命名等。inode_operations结构体还包含对文件属性的操作函数等。

inode_operations结构体实例由具体文件系统类型实现,在创建inode实例时由具体文件系统类型代码将inode_operations实例指针赋予inode实例i_op成员。inode_operations结构体体定义在/include/linux/fs.h头文件:

struct inode_operations 	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);  //查找子目录 	const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *); //inode表示符号链接,用于查找链接文件	int (*permission) (struct inode *, int);  //文件访问权限检查	struct posix_acl * (*get_acl)(struct inode *, int);  	int (*readlink) (struct dentry *, char __user *,int);	int (*create) (struct inode *,struct dentry *, umode_t, bool); //创建普通文件	int (*link) (struct dentry *,struct inode *,struct dentry *); //创建链接	int (*unlink) (struct inode *,struct dentry *);	int (*symlink) (struct inode *,struct dentry *,const char *);	int (*mkdir) (struct inode *,struct dentry *,umode_t);  //创建目录	int (*rmdir) (struct inode *,struct dentry *);          //删去目录	int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);//创建设备节点,命名管道等	int (*rename) (struct inode *, struct dentry *,  //重命名			struct inode *, struct dentry *, unsigned int);	int (*setattr) (struct dentry *, struct iattr *);   //用于设置文件属性,在chmod()和相关的系统调用中调用	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);//用于获取文件属性,在stat()和相关的系统调用中调用	ssize_t (*listxattr) (struct dentry *, char *, size_t);//用于列出给定文件所有的扩展属性	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,		      u64 len);	int (*update_time)(struct inode *, struct timespec *, int); //用于更新特定时间和inode的i_version	int (*atomic_open)(struct inode *, struct dentry *,			   struct file *, unsigned open_flag,			   umode_t create_mode, int *opened);	int (*tmpfile) (struct inode *, struct dentry *, umode_t);	int (*set_acl)(struct inode *, struct posix_acl *, int);} ____cacheline_aligned;

lookup:在inode表示目录文件内容中查找子目录项,并填充dentry实例(已创建dentry实例,实例中包含目录项(文件)名称,用于查找),这是一个非常重要的函数,在此函数内需要创建inode实例并初始化。

file_operations结构体主要包含对文件内容的操作函数指针,是内核操作文件的主要接口,是内核中非常重要的一个数据结构。因为外部设备也被视为文件(设备文件),file_operations结构体也是内核操作外部设备的接口,设备驱动程序的主要工作就是实现file_operations结构体实例。

file_operations结构体与inode_operations结构体一样由具体文件系统类型实现,在打开文件创建inode实例时将结构体实例指针赋予inode实例i_fop成员。file_operations结构体定义在/include/linux/fs.h头文件内,这个我们太熟悉了,就不多做介绍了。

4.2 节点操作

内核为inode结构体建立了slab缓存和全局散列表inode_hashtable,实例还关联到超级块中双链表。inode实例与dentry实例关联,添加到内核根文件系统结构中,文件系统超级块super_block结构体中包含两个inode实例双链表,表头分别是super_block.s_inode_lru(LRU链表)和super_block.s_inodes。

内核在搜索文件时为每个目录项dentry实例创建关联的inode实例(或关联到现有inode实例),并将inode实例添加到全局散列表和超级块s_inodes双链表中,释放inode实例时,暂时不用的实例将被添加到超级块s_inode_lru双链表中。

void __init vfs_caches_init(void){	//生成names_cache slab缓存,是VFS为保存路径path信息,而临时使用的内存空间缓存	//可以通过__getname()/__putname()等函数使用names_cache slab缓存	names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,			SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);	//为支持对目录项的缓存做准备,生成目录项的slab缓存后,构建散列表进行管理	//此外设置系统可用内存不足时回收目录项缓存的方式	dcache_init();		//对索引节点执行缓存做准备,和初始化目录项一样,生成索引节点的slab缓存,构建	//散列表后设置回收方式	inode_init();	//VFS使用file结构体显示系统打开的文件,为了管理file结构体	files_init();	files_maxfiles_init();	//Linux系统中所有的文件系统通过VFS层向用户显示,被挂载的文件系统通过vfsmount结构体显示,该结构体还显示VFS层	//生成vfsmount结构体的mnt_cacha_slab缓存,虚拟系统则初始化sysfs,rootfs	mnt_init();	//Linux将与系统连接的所有设备显示为文件,这些文件就是设备文件,注册块设备驱动	bdev_cache_init();	//字符设备初始化	chrdev_init();}

我们这里主要关注节点初始化,该接口主要是生成索引节点的slab缓存并构建散列表

void __init inode_init(void){	unsigned int loop;	//生成索引节点结构体inode_cacha_slab缓存	inode_cachep = kmem_cache_create("inode_cache",					 sizeof(struct inode),					 0,					 (SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|					 SLAB_MEM_SPREAD|SLAB_ACCOUNT),					 init_once);	/* Hash may have been set up in inode_init_early */	if (!hashdist)		return;	//生成散列表管理各索引节点对象并进行初始化	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]);}

标签: #mysqlcantrmdir