创作人QQ:851301776,邮箱:lfr890207@
欢迎大家一起技术交流,本博客主要是自己学习的心得体会,只为每天进步一点点!
个人座右铭:
1.没有横空出世,只要厚积一定发。
2.你可以学历不高,你可以不上学,但你不能不学习
一、文件系统概述
在Linux操作系统中,一切皆是文件,除了通常所说狭义的文件(文本文件和二进制文件)以外,目录、套接字、设备及管道等都是文件。
文件系统在不同的上下文中有不同的含义。在存储设备缓存文件的方法数据结构和访问方法。按照某种文件系统类型格式化的一块存储介质,内核中负责管理和存储文件的模块,即文件系统,Linux文件系统的架构,分为用户空间、内核空间、硬件3个层面。
1、用户空间
应用程序可以直接使用内核提供的系统调用访问文件:
(1)一个存储设备上的文件系统,只有挂载到内存中目录树的某个目录下,进程才能访问这个文件系统。
(2)系统调用umount用来卸载某个目录下挂载的文件系统。可以执行命令“umount dir”来卸载文件系统,umount命令调用系统调用umount。
备注:
执行命令:mount -t fstype divice dir, 把文件系统挂载到某个目录下,卸载某个目录挂载的执行命令:umount dir。 然后可以执行其他系统调用:open/close/read/write/lseek/fsync/fdatasync.
应用程序可以使用glibc库封装标准I/o流函数访问文件,标准I/o流提供缓冲区,目的尽可能减少调用read/write次数,提高性能。标准I/O流函数:fopen/fclose/fread/fwrite(fflush)/flseek
2、硬件层面
外部存储设备分为块设备、闪存和NVDIMM(非易失性内存)设备3类。块设备主要有2种类型:机械硬盘和闪存类块设备。
机械硬件读写单位为扇区,访问首先沿着半径方向移动磁头寻找磁道,然后转动盘片找到扇区。
闪存作为存储设备,里面的控制器运行固化驱动程序,驱动程序的功能是内存转换层,把闪存转换为块设备,对外表现为块设备。Solid state drives,SSD。手机/平板嵌入式存储卡eMMC(embedded multi meida card)/通用闪存存储UFS(Universal flash storage)。
3、内核空间
在内核的目录fs下可以看到,内核支持多种文件系统类型。为了对用户程序提供统一的文件操作系统接口,为了使不同的文件系统实现能够共存,内核实现一个抽象层,称为虚拟文件系统(Virtual File System VFS),也称为虚拟文件系统切换(Virtual Filesystem Switch,VFS)。
文件系统分为:块设备文件系统(存储设备十二机械硬盘和SSD等块,EXT2/3/4)、闪存文件系统存储设备Nor,NAND闪存、内存文件系统(文件在内存中)、伪文件系统(假的文件系统)。
二、虚拟文件系统数据结构
虽然不同文件系统类型的物理结构不同,但是虚拟文件系统定义一套统一的数据结构。超级块、索引节点、目录项。
1、超级块
文件系统的第一块是超级块,用来描述文件系统的总体信息。当我们把文件系统挂载到内存中的目录树的一个目录下时,就会读取文件系统的超级块,在内存中创建超级块的副本,结构体super_block内核源码,主要成员如下:
超级块操作系统集合的数据结构时:结构体super_operations,主要成员如下:
2、挂载描述符
一个文件系统,只有挂载到内存中目录树的一个目录下,进程才能访问这个文件系统。每次挂载文件系统,虚拟文件系统就会创建一个挂载描述符:mount 结构体。挂载描述符用来描述文件系统的一个挂载实类,同一个存储设备上的文件系统可以多次挂载,每次挂载到不同的目录下。
结构体mount挂载描述符的主要成员如下:
3、文件系统类型
因为每种文件系统类型的超级块的格式不同,所以每种文件系统需要向虚拟文件系统注册文件系统类型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 ha
ndle 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;
};
4、索引节点
在文件系统中,每个文件对应一个索引节点,索引节点描述两类信息:
(1)文件的属性,也称为元数据(metadata)。
(2)文件数据的存储位置,每个索引节点有一个唯一的编号。当内核访问存储设备上的一个文件时,会在内存中创建索引节点的一个副本,内核结构体inode源码如下:
struct inode {
umode_t i_mode; // 文件类型和访问权限
unsigned short i_opflags;
kuid_ti_uid; // 创建文件的用户的标识符
kgid_ti_gid; // 创建文件的用户所属的组标识符
unsigned inti_flags;
#ifdef CONFIG_FS_POSIX_ACLstruct 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
/* 索引节点编号 */
unsigned longi_ino;
/*
* Filesystems may only read i_nlink directly. They shall u
se 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_ti_rdev;// 设备号
loff_ti_size; // 文件长度
struct timespec i_atime; // 上一次访问文件的时间
struct timespeci_mtime; // 上一次修改文件数据的时间
struct timespec i_ctime; // 上一次修改文件索引节点的赶时间
spinlock_ti_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned shorti_bytes; // 文件长度除以块长度的余数
unsigned inti_blkbits; // 块长度以 2 为底的对数,块长度是 2 的 i_blkbits 次幂
blkcnt_ti_blocks; // 文件的块数
#ifdef __NEED_I_SIZE_ORDERED
seqcount_ti_size_seqcount;
#endif
/* Misc */
unsigned longi_state;
struct rw_semaphore i_rwsem;unsigned long
dirtied_when; /* jiffies of first dirtying*/
unsigned longdirtied_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() */
inti_wb_frn_winner;
u16i_wb_frn_avg_time;
u16i_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;
};
u64i_version;
atomic_ti_count;
atomic_ti_dio_count;
atomic_ti_writecount;
#ifdef CONFIG_IMA
atomic_ti_readcount; /* struct files open RO */
#endif
const struct file_operationsi_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;
unsignedi_dir_seq;
};
__u32i_generation;
#ifdef CONFIG_FSNOTIFY
__u32i_fsnotify_mask; /* all events this inode caresabout */
struct fsnotify_mark_connector __rcu *i_fsnotify_marks;
#endif
#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
struct fscrypt_info *i_crypt_info;
#endif
voidi_private; /fs or device private pointer */
};
5、目录项
文件系统把目录当做文件,这种文件的数据是由目录项结构组成的,每个目录项存储一个子目录或文件的名称以及对应的索引节点号。当内核访问存储设备上的一个目录项时,会在内核中创建目录项的一个副本,结构体dentry主要成员如下:
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; /* 用来把目录项加入散列表 */
struct dentryd_parent; /指向父目录 */
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_blockd_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_td_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;
};
6、文件打开实例及打开文件表
当进程打开一个文件的时候,虚拟文件系统就会创建一个打开实例:file结构体
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
/*struct path {
struct vfsmount *mnt; // 指向文件所属文件系统的挂载描述符的成员 mnt
struct dentry *dentry; // 文件对应的目录项
};*/
struct path f_path; // 存储文件在目录树中的位置
struct inodef_inode; /指向文件的索引节点 */
// 指向文件操作命令
const struct file_operations *f_op;
/** Protects f_ep_links, f_flags.* Must not be taken from IRQ context.*/
spinlock_tf_lock;
atomic_long_tf_count;
unsigned intf_flags;
fmode_tf_mode; // 访问模式
struct mutexf_pos_lock;
loff_tf_pos; // 文件偏移,进程当前正在访问的位置
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64f_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 */
文件系统信息结构主要成员如下:
打开文件表的数据结构如下:
三、注册文件系统类型
因为每种文件系统的超级块的格式不同,所以每种文件系统需要向虚拟文件系统注册文件类型:file_system_type,实现mount方法来读取和解析超级块,函数register_filesystem来注册文件系统类型:
管理员可以执行命令:cat /proc/filesystems--查看已经注册的文件系统类型。