Linux文件系统学习笔记
文件的存储
- 分两部分
纯数据区
- 文件真正的数据存储区、基本存储单位为block
元数据区
- 文件属性:磁盘中的存储位置、文件长度等信息
- 时间戳:创建时间、修改时间
- 读写权限:使用read/write系统调用时,要首先要进行权限检查
- 所属组、所有者
- 链接数
文件索引节点 inode
用来存储文件信息
- inode:每个文件使用一个inode结构体来描述
- 每个inode有固定编号、有单独的存储空间
- 每个inode的大小为128/256B
- Linux系统根据inode来查找文件的存储位置
TIPS
- 查看文件的inode信息:$ stat xx.c
- 查看某个分区的inode总数:$ df -i
- 查看inode大小:$ dumpefs -h /dev/sda1
data block
数据块(逻辑块)
- 格式化磁盘时划分的文件系统的最小逻辑读写单位
- 每个block都有自己的编号,inode中存放文件的block地址信息
- 一个block一般是扇区或页的整数倍:1K/2K/4K
TIPS
- 查看某个分区的block信息:$ df
超级块super block
- 记录整个文件系统信息
- 一个inode、block的大小
- inode使用情况:已使用数量、未使用数量
- block使用情况:已使用数量、未使用数量
- 文件系统挂载情况
- 文件系统的挂载时间、最后一次写入数据、检验磁盘的时间
- 当文件系统挂载时,这部分信息会加载到内存,并常驻内存
磁盘格式化
两种格式化
物理格式化
- 磁盘在使用前要进行分区和格式化:MBR中存放分区信息、开机代码
- 出厂前厂家已经做好的工作:划分磁道、扇区
逻辑格式化
- 使用格式化工具,在磁盘上安装文件系统
- 将磁盘划分为不同的block
- 将磁盘划分为不同的区段
不同的区段
- boot sector:引导扇区
- Superblock:记录文件系统的整体信息、inode和block信息
block group
- 每个block group都有一个group descriptor,聚集存储在分区开头位置
- block bitmap:记录block的使用情况,哪些在使用,哪些是空的
- inode bitmap:记录inode的使用情况
- inode table、data block
块组
block group
- 一个分区在格式化时,可以划分为多个block group
- 每个block group包含block bitmap、 inode bitmap、 inode table、 data block
- 每个block bitmap大小为一个block,每bit表示一个block
- 每个inode bitmap大小为一个block,每一个bit表示一个inode
group descriptor
- 存储在superblock的后面
- 有一个block指针,指向block bitmap
- 有一个block指针,指向inode bitmap
- 有一个block指针,指向inode table
- group descriptor信息存储在superblock中: group descriptor总数等信息
目录和目录项
目录
- 目录是一个文件,有自己的inode,在inode中将该文件类型标记为“目录”
- 目录存储在data block中
- 目录本质上是一个表格:由若干个目录项组成
- 一个目录下面可以多个文件:文件名和文件对应的inode
- 一个目录文件有多个子目录:目录名和其对应的inode
- 多个子目录构成树状的文件系统结构
目录项
- 一个目录项由文件名和inode编号组成,根据inode编号可以找到inode table中真正的inode节点
- 目录项存储在data block中
文件路径
构成
- 由各个目录、子目录构成
- 各个路径构成树状结构的文件系统
分类
- 相对路径
- 绝对路径
绝对路径
根目录
- 绝对路径的参考起点
- Linux内核中的“/”
- Windows系统中的盘符
- 文件系统预留的inode编号:2
相对路径
当前目录
- 相对路径的参考起点
- . :当前目录的硬链接
- … :上级目录的硬链接
- 根目录下的.和…
- 查看当前目录的inode编号:$ ls –i –d .
小结
- 一个目录下可以包含多个文件、或者嵌套多个子目录
- 各级目录构成一个路径,应用程序根据该路径来找到文件
- 路径分为绝对路径和相对路径
- 路径的本质:各级目录文件中的目录构成的一个inode链
文件系统的挂载
基本原理
- 一个磁盘格式化、安装文件系统后,可以通过文件接口访问存储空间
- 用户通过路径名来访问文件
- 挂载:让磁盘与Linux根文件系统某个目录建立关联、加入全局文件系统树
- 挂载点(mount point)是进入该挂载设备文件系统的入口
实验
- mount --bind udisk/ mnt/
挂载过程
- 结构体:vfsmount\superblock
- 每个挂载的文件系统,VFS都会创建一个vfsmount、super_block对象
- 该对象描述了文件系统mount的所有信息
- 父文件系统的挂载点:vfsmount->mnt_mountpoint = /mnt
- 子文件系统的根目录:vfsmount->mnt_root = superblock->s_root
- 初始化好vfsmount对象后,将该对象添加到VFSMOUNT hash table(哈希表) 中。
文件路径解析
目录项
- 若目录项dentry标记为DCACHE_MOUNTED,路径解析时对该目录/mnt项屏蔽
- 计算该目录的HASH值,根据值去VFSMOUNT hash table查找对应的vfsmount对象
- 根据vfsmount->mnt_root,找到子文件系统的根目录
- 查找子文件系统指定目录下的文件
文件系统类型
基于操作系统
基于Linux/android
- ext/ext2/ext3/ext4、XFS、brtfs、iso9660、JFFS/JFFS2
- minix、proc、NFS、SMB、swap
- CRAMFS、yaffs、yaffs2、UBIFS
基于Windows
- FAT16/FAT32
- NTFS
基于Mac OS X
- HFS
Linux下的文件系统
按存储介质划分
基于磁盘/Flash
- Ext2/ext3/ext4
- JFFS2、UBIFS、CRAMFS
- 基于Flash的文件系统一般基于MTD驱动:坏块管理、磨损均衡
基于内存
- ramdisk : ramfs、tmpfs
其它特殊文件系统
- procfs、sysfs
- NFS:network file system
- devfs
文件系统选择
性能指标
- 挂载时间
- IO性能:顺序/随机读写能力、IO等待时间、大/小文件读写能力
- 资源利用率:存储空间利用率、CPU利用率
- 功耗
嵌入式文件系统
组合文件系统
- 根据存储数据不同划分不同分区:系统、配置、数据、多媒体数据
- 内存文件系统:ramdisk、proc
- 特殊文件系统:devfs、sysfs
虚拟文件系统(VFS)
- visual file system
- 基于内核和存储设备之间的抽象层
- 向上:统一封装了不同设备、文件系统的读写接口:系统调用API
- 向下:新的设备、文件系统添加到Linux内核,实现VFS的接口即可
- Linux内核支持60+种不同类型的文件系统
VFS的对象类型
对象属性
- super_block:已经安装的具体的文件系统
- inode:代表一个具体的文件
- dentry:目录项,目录项路径的组成部分
- file:进程打开的文件
对象方法
- super_operations:alloc_inode、write_inode、destroy_inode
- inode_operations:link、mknod、mkdir、rename、create
- dentry_operations:d_hash、d_compare、d_delete、d_release
- file_operations:read、write、open、close、fsync、mmap
通过OOP理解VFS
VFS中的面向对象思想
- 封装:file->file_operations
- 继承:write_inode、ext2_write_inode
- 多态:callback
文件的存储
- 磁盘:扇区、簇、superblock、block、inode、dentry
- Flash:页、块、block、inode、dentry
- VFS:统一操作接口
- 系统调用接口:open、close、read、write、fsync
- C标准库函数:fopen、fclose、fread、fwrite、fsync
程序中如何操作文件
文件描述符
- Linux进程使用文件描述符(file descriptors,简称fd)来操作文件
- int fd = open (“/home/wit/hello.txt”, O_WRONLY, 0666);
- read (fd, buf, 100);
- 结构体files_struct
- 用来表示一个进程打开的文件列表
struct files_struct {
atomic_t count;
bool resize_in_progress;
wait_queue_head_t resize_wait;
struct fdtable __rcu *fdt;
struct fdtable fdtab; int next_fd;
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
文件
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u; //所有打开的文件构成一个系统级的全局双链表
struct path f_path;
struct inode *f_inode; //inode节点
const struct file_operations *f_op; //该文件的读写方法
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;
void *private_data;
struct address_space *f_mapping;
};
一个进程打开的文件
C语言中的文件指针FILE*
对文件描述符的封装
- 文件描述符:fd
- 文件位置:f_fops
- 缓冲区
- 文件标志:f_mod
typedef struct _IO_FILE FILE;
struct _IO_FILE {
int _flags;
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
};
标准流
文件描述符 | 用途 | POSIX名称 | stdio流 |
---|---|---|---|
0 | 标准输入 | STDIN_FILENO | stdin |
1 | 标准输出 | STDOUT_FILENO | stdout |
2 | 标准错误 | STDERR_FILENO | stderr |
硬链接与软链接
链接
- Linux下一种文件共享的方式,类似于Windows下的快捷方式
- 一个文件使用多个别名:多个文件名共享一个inode
硬链接
- 硬链接和文件有相同的inode和datablock
软链接
- 软链接是一个普通文件,文件内容为:指向文件的路径名
- 有自己的inode编号和data block、文件权限、属性…
- 删除软链接不会影响到其指向的文件本身
硬链接与软链接区别
硬链接
- 只能对存在的文件创建硬链接
- 不能对目录创建硬链接
- 创建硬链接不能跨越文件系统分区
软链接
- 可以对不存在的文件或目录创建软链接
- 可以对目录创建软链接
- 可以跨越文件系统分区创建软链接
命令
FS相关
- mount
- mkdir/rmdir/chmod/chown/
- df、du、wc
磁盘管理
- 统计磁盘使用率:$ df -h
- 统计目录:$ du
文件统计
- 当前目录下的C文件个数: $ find . -name “*.c” | wc -l
- 当前目录下(包括子目录)的文件个数:$ ls –lR | grep “^-” | wc –l
- 一个项目的总目录个数:$ ls –lR | grep “^d” | wc –l
- 一个项目的代码总行数:$ find . -name “*.c” |xargs cat | wc -l
磁盘格式化及挂载
格式化、分区
fdisk /dev/sdb
按m显示菜单
按n添加分区
按p设置为主分区,e设置为扩展分区
输入分区号
第一个扇区大小(默认2048,2k)
...一路默认
按w保存
ls /dev/sdb*
安装文件系统
mkfs.ext4 /dev/sdb1
挂载
mount -t ext4/ dev/sdb1 /mnt
恢复删除的文件
rm命令
文件删除的背后
- 并没有真正删除数据,当inode的链接计数>1时
- 仅删除了文件名和inode之间的关联:清除了目录项中inode指针信息,
真正的文件删除
- 当inode的链接计数为1时
- 将目录项中的文件名和inode对应关系删除
- 将该文件inode中的block指针删除
- 在inode bitmap中将该文件的inode标记为未用
- 将block bitmap中将该文件的data block标记为未用
- ext日志文件系统:把删除文件的inode信息和文件名写入日志
恢复删除的文件
- apt-get install extundelete
- extundelete --inode 2 /dev/sdb1
- extundelete /dev/sdb1 --restore-file /home/wit/hello.c
- extundelete /dev/sda1 --restore-inode 1122
- extundelete /dev/sdb1 --restore-directory /home/wit/test