【前言】
文件系统是结合了众多工程师智慧的结晶,所以在这个过程中,会出现各种各样的小技术手段来解决一些实际性问题,从而产生了很多技术词汇。
【文件系统Overview】
Linux一切皆文件:普通文件、目录文件(也就是文件夹)、设备文件、链接文件、管道文件、套接字文件(数据通信的接口)等等。引入文件系统,帮助我们屏蔽底层实现的细节(不必care扇区,cache,文件管理),我们只需通过VFS提供的fs_op就可以对文件进行对应的操作。
Android 支持的文件系统: ext4 vfat f2fs sdcardfs fuse(fuse解决了文件系统必须在内核态的难题。将文件系统的实现从内核态搬到了用户态)
Android linux 文件系统核心: VFS(1.向上提供接口,向下兼容文件系统,实现了inode/page cache等公共部分,让其他文件系统无需重复实现)
【文件系统四大对象】
1. 超级块super_block:
struct super_block {
struct list_head s_list; /* Keep this first 指向超级块链表的指针*/
dev_t s_dev; /*具体文件系统的块设备描述符*/
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; /*指向super_block操作的函数集合*/
alloc_inode () /*创建并初始化一个inode*/
write_inode() /*将inode同步到磁盘*/
sync_fs() /*同步文件系统元数据到磁盘*/
list_head s_inodes; /* all inodes */
list_head s_inodess_dirty; /* dirty inodes */
void *s_fs_info; /* Filesystem private info 具体文件系统的私有数据*/
*** }
- 超级块用来描述整个文件系统的信息
- 每个具体的文件系统都有自己的超级块
- VFS超级块是各种文件系统在安装时建立的,并在卸载时被自动删除,其数据结构是super_block
- 所有超级块对象都以双向循环链表的形式链接在一起
1.1 struct super_operations:超级块操作表
2. inode:index node
struct inode {
struct hlist_node i_hash; /* 哈希表 */
struct list_head i_list; /* 索引节点链表 */
struct list_head i_dentry; /* 目录项链表 */
unsigned long i_ino; /* 节点号 */
atomic_t i_count; /* 引用记数 */
umode_t i_mode; /* 访问权限控制 */
unsigned int i_nlink; /* 硬链接数 */
uid_t i_uid; /* 使用者id */
gid_t i_gid; /* 使用者id组 */
kdev_t i_rdev; /* 实设备标识符 */
loff_t i_size; /* 以字节为单位的文件大小 */
struct timespec i_atime; /* 最后访问时间 */
struct timespec i_mtime; /* 最后修改(modify)时间 */
struct timespec i_ctime; /* 最后改变(change)时间 */
unsigned int i_blkbits; /* 以位为单位的块大小 */
unsigned long i_blksize; /* 以字节为单位的块大小 */
unsigned long i_version; /* 版本号 */
unsigned long i_blocks; /* 文件的块数 */
unsigned short i_bytes; /* 使用的字节数 */
spinlock_t i_lock; /* 自旋锁 */
struct rw_semaphore i_alloc_sem; /* 索引节点信号量 */
struct inode_operations *i_op; /* 索引节点操作表 */
struct file_operations *i_fop; /* 默认的索引节点操作 */
struct super_block *i_sb; /* 相关的超级块 */
struct file_lock *i_flock; /* 文件锁链表 */
struct address_space *i_mapping; /* 相关的地址映射 */
struct address_space i_data; /* 设备地址映射 */
struct dquot *i_dquot[MAXQUOTAS]; /* 节点的磁盘限额 */
struct list_head i_devices; /* 块设备链表 */
struct pipe_inode_info *i_pipe; /* 管道信息 */
struct block_device *i_bdev; /* 块设备驱动 */
unsigned long i_dnotify_mask; /* 目录通知掩码 */
struct dnotify_struct *i_dnotify; /* 目录通知 */
unsigned long i_state; /* 状态标志 */
unsigned long dirtied_when; /* 首次修改时间 */
unsigned int i_flags; /* 文件系统标志 */
unsigned char i_sock; /* 可能是个套接字吧 */
atomic_t i_writecount; /* 写者记数 */
void *i_security; /* 安全模块 */
__u32 i_generation; /* 索引节点版本号 */
union {
void *generic_ip; /* 文件特殊信息 */
} u;
- 文件系统处理文件所需要的所有信息都保存在称为索引节点的inode结构体中
- 同一个文件系统中,每个文件的索引节点号都是唯一的
- 与索引节点关联的方法由struct inode_operations来描述
- inode有两个设备号:i_dev(常规文件的设备号),i_rdev(某一设备的设备号)
- LInux文件系统的另外一大特色:设备即文件 —— 驱动中设备号的来源。
2.1 struct inode_operations:index node操作函数
3. dentry:目录项
struct dentry {
atomic_t d_count; //目录项对象使用计数器,可以有未使用态,使用态和负状态
unsigned int d_flags; //目录项标志
struct inode *d_inode; //与文件名关联的索引节点
struct dentry *d_parent; //父目录的目录项对象
struct list_head d_hash; //散列表表项的指针
struct list_head d_lru; //未使用链表的指针
struct list_head d_child; //父目录中目录项对象的链表的指针
struct list_head d_subdirs; //对目录而言,表示子目录目录项对象的链表
struct list_head d_alias; //相关索引节点(别名)的链表
int d_mounted; //对于安装点而言,表示被安装文件系统根项
struct qstr d_name; //文件名
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op; //目录项方法
struct super_block *d_sb; //文件的超级块对象
vunsigned long d_vfs_flags;
void *d_fsdata; //与文件系统相关的数据
unsigned char d_iname [DNAME_INLINE_LEN]; //存放短文件名
};
- 存在于内存的目录项缓存,为了提高查找性能而设计,动态生成的(哈希表,或组织为一颗树,链表)。
- 每个文件除了一个struct inode结构体外,还要一个目录项struct dentry结构,通过其d_hash域链入哈希表中
- 不管是文件夹还是文件(目录),都属于目录项,所有的目录项在一起构成一颗庞大的目录树。
- dentry对象有三种状态:被使用,未被使用和负状态。
3.1 struct dentry_operations
(未完待续)
PS:
【I/O 缓冲区】
Cache:解决的是速度不同步的问题。
- Buffer : 用于内存和硬盘的缓冲,缓冲“写”操作,保存即将要写入到磁盘上的数据。
- Cache:一般指高速缓存,用于CPU和内存之间的缓冲,解决读的问题,保存从磁盘上读出的数据。
Buffer Cache和 Page Cache
- buffer cache:块缓冲器,面向块设备(文件系统的块)。
- page cache: 页缓冲器,面向虚拟内存。已映射到内存的某些物理设备(例如磁盘)上的数据,包含来自最近访问的“文件”的整个页面。在页面I / O操作(例如read()])中,内核检查数据是否驻留在page cache中。如果数据在page cache中,则内核可以快速返回请求的页面,而不必从磁盘读取数据。