Skip to content

文件系统 (File System)

一、文件系统概述

什么是文件系统

文件系统 (File System) 是操作系统中负责管理和存储文件的软件模块,提供: - 数据的持久化存储 - 按名访问 (文件名 → 数据) - 组织 (目录树、层级) - 保护 (权限、所有者) - 共享 (多进程访问)

文件系统的关键概念

  • 文件 (File):命名的字节序列
  • 目录 (Directory):特殊的文件,包含文件名到 inode 的映射
  • 元数据 (Metadata):关于文件的信息(大小、时间、权限)
  • 数据 (Data):文件实际内容
  • inode (索引节点):文件的元数据 + 数据位置
  • 超级块 (Superblock):文件系统的全局信息
  • 块/扇区 (Block/Sector):磁盘读写单位
  • 挂载点 (Mount Point):把 FS 接到目录树

文件 vs 文件系统

概念 文件 文件系统
本质 命名的数据集合 数据的组织管理方式
标识 文件名 文件系统类型(Ext4)
例子 /etc/passwd Ext4、XFS、Btrfs

二、文件分类

1. 按内容分类

类型 例子
普通文件 文本、二进制、可执行
目录文件 /home、/var
链接文件 软链接、硬链接
设备文件 /dev/sda、/dev/null
管道文件 FIFO / Pipe
Socket Unix Domain Socket

2. Linux 文件类型 (ls -l)

字符 类型 例子
- 普通文件 .txt、.c
d 目录 /home
l 符号链接 /usr/bin/python
b 块设备 /dev/sda
c 字符设备 /dev/tty
p 管道 /tmp/mypipe
s Socket /var/run/docker.sock

三、文件结构

1. 字节序列 (Unix 风格)

  • 文件 = 字节数组
  • OS 不解释内容
  • 灵活(任何内容)
  • 缺点:无结构

2. 记录序列

  • 文件 = 记录 (Record) 序列
  • 适合数据库
  • 缺点:不灵活

3. 树 (Tree)

  • 文件 = 树 (B-Tree, B+Tree)
  • 适合按 key 查找
  • 数据库常用

四、文件访问方式

1. 顺序访问 (Sequential)

  • 按字节顺序读
  • 磁带模型
  • 大多数文件

2. 直接访问 (Direct/Random)

  • 可跳到任意位置
  • 磁盘模型
  • 数据库
  • lseek() 实现

3. 索引访问 (Indexed)

  • 文件 = 索引 + 数据
  • 适合大型文件

五、文件元数据与属性

1. 文件元数据 (stat)

$ stat /etc/passwd
  File: /etc/passwd
  Size: 2746       Blocks: 8          IO Block: 4096
Device: 802h/2050d Inode: 1577308     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)
Modify: 2024-01-15 10:30:00
Change: 2024-01-15 10:30:00
 Birth: 2023-09-20 14:00:00

2. stat 结构体 (C)

struct stat {
    dev_t     st_dev;       // 设备
    ino_t     st_ino;       // inode 号
    mode_t    st_mode;      // 类型 + 权限
    nlink_t   st_nlink;     // 硬链接数
    uid_t     st_uid;       // 属主 UID
    gid_t     st_gid;       // 属组 GID
    dev_t     st_rdev;      // 设备文件
    off_t     st_size;      // 大小
    blksize_t st_blksize;   // 块大小
    blkcnt_t  st_blocks;    // 占用块数
    time_t    st_atime;     // 访问时间
    time_t    st_mtime;     // 修改时间
    time_t    st_ctime;     // 元数据修改时间
};

3. 文件属性查看命令

ls -l             # 基础
lsattr            # 扩展属性 (chattr +i)
file <file>       # 类型探测
stat <file>       # 详细
getfacl <file>    # ACL

六、inode (索引节点) - 核心概念

1. 什么是 inode

inode = index node (索引节点)

  • 文件的"元数据 + 数据位置"
  • Linux 文件系统最核心的概念
  • 文件名只是 inode 的人类友好别名
  • 一个文件 = 一个 inode + 数据块

2. inode 内容

struct ext4_inode {           // 简化
    __le16  i_mode;          // 文件类型 + 权限
    __le16  i_uid;           // 属主
    __le32  i_size;          // 文件大小
    __le32  i_atime;         // 访问时间
    __le32  i_ctime;         // 状态改变时间
    __le32  i_mtime;         // 数据修改时间
    __le32  i_dtime;         // 删除时间
    __le16  i_gid;           // 属组
    __le16  i_links_count;   // 硬链接数
    __le32  i_blocks;        // 占用的块数
    __le32  i_flags;
    __le32  i_block[15];     // 数据块指针(0-11 直接,12 间接,13 二级间接,14 三级间接)
    ...
};

3. inode 与文件名

        目录文件                inode 表
        /home/                  ┌─────────┐
        ├── user1/              │ inode 1 │  ──→  数据块
        │   ├── a.txt          ├─────────┤
        │   └── b.txt          │ inode 2 │  ──→  数据块
        └── user2/              ├─────────┤
            └── c.txt          │ inode 3 │  ──→  数据块
                                └─────────┘

        a.txt ──→ 目录项(name=inode) ──→ inode

关键点: - 文件名存放在目录中(目录是"文件名 → inode 号"的映射表) - inode 存放在inode 表中 - 同一文件可有多个文件名(硬链接) - 删除文件只是删除目录项,inode 还在(直到链接数=0 才释放)

4. inode 大小和数量

  • inode 大小:Ext4 默认 256 字节
  • inode 数量:格式化时确定(每 N 字节一个 inode)
  • 查看:df -i 看 inode 使用率
  • inode 耗尽:磁盘还有空间但不能创建文件(常见小文件服务器)

5. 软链接 vs 硬链接

维度 硬链接 软链接 (符号链接)
实质 同一个 inode 的多个目录项 独立文件,内容是目标路径
跨文件系统 ❌ 否 ✅ 是
链接目录 ❌ 否(防止循环) ✅ 是
删除原文件 链接数减 1,文件不删 链接失效 (悬空符号链接)
inode 相同 不同
ln file1 file2 硬链接 ln -s file1 file2 软链接
大小 占 inode 占一个文件块(几字节 ~ 几十字节)
$ ln /etc/passwd /tmp/passwd.hard
$ ln -s /etc/passwd /tmp/passwd.soft
$ ls -li /etc/passwd /tmp/passwd*
-rw-r--r-- 2 root root 2746 /etc/passwd
lrwxrwxrwx 1 root root   11 /tmp/passwd.soft -> /etc/passwd
-rw-r--r-- 2 root root 2746 /tmp/passwd.hard
#              ↑             ↑  inode 相同
#  /tmp/passwd.soft 大小是 11 字节(路径长度)

七、目录与路径

1. 目录结构

  • Linux:树形,从 / 开始
  • Windows:森林结构,每个盘符是一棵树
/
├── bin/         # 基本命令
├── etc/         # 配置文件
├── home/        # 用户
├── usr/         # 用户程序
├── var/         # 变化数据
└── proc/        # 内核信息

2. 路径

  • 绝对路径:从根开始,如 /etc/passwd
  • 相对路径:从当前目录开始,如 ../etc/passwd
  • 特殊目录:
  • . 当前目录
  • .. 上级目录
  • ~ 用户主目录
  • - 上次所在目录

3. 解析过程

shell 解析:cd /usr/local/bin
1. / 根目录
2. 进入 usr 子目录
3. 进入 local 子目录
4. 进入 bin 子目录
每步都查该目录的 inode 指向,获取子项

4. PATH 环境变量

  • 可执行文件的搜索路径
  • : 分隔
  • . 表示当前目录(不安全,慎用)
echo $PATH
# /usr/local/bin:/usr/bin:/bin:...

which command   # 找命令位置

八、文件描述符 (File Descriptor)

1. 什么是文件描述符

文件描述符 (fd):进程打开文件时,内核返回的非负整数作为引用句柄

  • fd 是进程级的(每个进程有独立的 fd 表)
  • 0 = stdin,1 = stdout,2 = stderr
  • 数量有限:ulimit -n (默认 1024)

2. fd 内部结构

进程级:
  fd_table[0] ─→ struct file
  fd_table[1] ─→ struct file
  fd_table[2] ─→ struct file (共享?)
  fd_table[3] ─→ struct file
  ...

每个 struct file:
  - file_operations (open/read/write/...)
  - f_pos (文件位置)
  - f_path (路径)
  - f_inode → inode
  - ...

3. 关键 fd

fd 名称 用途
0 stdin 标准输入
1 stdout 标准输出
2 stderr 标准错误
3+ 用户打开 普通文件/网络

4. dup / dup2 / dup3

复制 fd:让多个 fd 指向同一文件

int newfd = dup(oldfd);                    // 复制,返回最小未用 fd
int newfd2 = dup2(oldfd, target_fd);       // 复制到指定 fd
int newfd3 = dup3(oldfd, target_fd, flags); // 支持 O_CLOEXEC

用途: - 重定向 stdin/stdout/stderr - shell > file 内部就是 dup2

5. /proc/PID/fd

$ ls -l /proc/$$/fd
lrwx------ 1 user user 64 12月 1 10:00 0 -> /dev/pts/0
lrwx------ 1 user user 64 12月 1 10:00 1 -> /dev/pts/0
lrwx------ 1 user user 64 12月 1 10:00 2 -> /dev/pts/0

九、文件 I/O 系统调用

1. 基本 I/O

// 打开/创建
int open(const char *path, int flags, mode_t mode);
// flags: O_RDONLY, O_WRONLY, O_RDWR
//        O_CREAT, O_EXCL, O_TRUNC, O_APPEND
//        O_NONBLOCK, O_CLOEXEC, O_SYNC
// 返回 fd

// 关闭
int close(int fd);

// 读
ssize_t read(int fd, void *buf, size_t count);
// 返回实际读到的字节数(0=EOF,-1=err)

// 写
ssize_t write(int fd, const void *buf, size_t count);

// 定位
off_t lseek(int fd, off_t offset, int whence);
// SEEK_SET, SEEK_CUR, SEEK_END

// 创建
int creat(const char *path, mode_t mode);  // open with O_WRONLY|O_CREAT|O_TRUNC

// 删除
int unlink(const char *path);   // 减链接数
int remove(const char *path);   // unlink 或 rmdir
int rmdir(const char *path);    // 删空目录

2. 高级 I/O

// 预读/强制写回
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
int fdatasync(int fd);
int fsync(int fd);          // 刷盘
int sync(void);             // 全系统刷盘

// mmap
void *mmap(void *addr, size_t length, int prot, int flags,
            int fd, off_t offset);
int munmap(void *addr, size_t length);
int msync(void *addr, size_t length, int flags);

// 异步 I/O (aio)
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);

// io_uring (现代)
struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring);
int io_uring_submit(struct io_uring *ring);
int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr);

3. 文件控制

// ioctl
int ioctl(int fd, unsigned long request, ...);

// fcntl
int fcntl(int fd, int cmd, ... /* arg */ );
// F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL
// F_GETLK, F_SETLK, F_SETLKW (锁)

// flock (建议锁)
int flock(int fd, int operation);
// LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB

十、文件锁 (File Locking)

1. 锁的必要性

  • 多进程同时操作同一文件
  • 需要互斥/协调

2. 锁的类型

类型 函数 范围 阻塞性
flock flock() 整个文件 阻塞/非阻塞
POSIX fcntl() 字节范围 阻塞/非阻塞
System V lockf() 字节范围 阻塞

3. 死锁问题

  • 多个锁 + 多个进程 = 可能死锁
  • 解决:统一顺序、超时回退

十一、Linux 文件系统架构

1. VFS (Virtual File System)

VFS:Linux 内核的"文件系统抽象层",为不同 FS 提供统一接口

应用:open/read/write
    ↓
VFS (统一接口)
    ↓
具体 FS:Ext4/XFS/Btrfs/NFS/proc/tmpfs
    ↓
块设备驱动 / 网络协议栈

2. VFS 的核心对象

对象 含义
super_block FS 的整体信息
inode 文件的元数据 + 数据位置
dentry 目录项(文件名 → inode)
file 进程打开的文件实例

3. 文件操作流程

用户态:open("/etc/passwd", O_RDONLY)
       ↓
VFS 路径查找:
  1. 解析路径 "/etc/passwd"
  2. 从挂载点的 dentry 树开始查找
  3. 一级级匹配,找到对应的 inode
       ↓
具体 FS 的 open 操作
       ↓
创建 struct file,分配 fd,返回

十二、主流 Linux 文件系统

1. Ext 系列

Ext2 (1993): - 无日志 - 适合闪存 / 启动分区

Ext3 (2001): - 加日志 - 兼容 Ext2

Ext4 (2008): - 主流,Ext3 升级 - 支持 1 EB 卷,16 TB 单文件 - extents 替代 block map - 多块分配 - 延迟分配 - journal checksum

Ext4 的关键特性:

特性 说明
Extent 用"起始块 + 长度"代替"块列表"
多块分配 一次分配多个连续块
延迟分配 写入时再决定分配位置
日志校验 防止日志损坏导致恢复失败
Online Defrag 在线碎片整理
Quota 用户/组配额

2. XFS

  • SGI 1994,后被 Linux 收编
  • RHEL/CentOS 默认
  • 适合大文件、大容量
  • B+Tree 索引
  • 分配组 (AG):并行 I/O
  • 在线扩容/收缩
  • 在线碎片整理

优势: - 高性能(尤其大文件) - 高可扩展(8 EB - 1 byte) - 适合数据库

3. Btrfs (B-tree FS)

  • Oracle 2007 开源
  • 写时复制 (CoW)
  • 快照 (snapshot)
  • 子卷 (subvolume)
  • 透明压缩 (zlib, lzo, zstd)
  • 去重 (online dedup)
  • 多设备: 内置 RAID
  • 校验和:数据完整性
  • 发送/接收:增量备份
  • Send/Recv

代表使用: - SUSE/openSUSE 默认 - Fedora 默认 - 阿里 Anolis OS

4. ZFS

  • Sun 2005
  • 写时复制
  • 池化存储
  • 快照、克隆
  • 内置 RAID-Z (类似 RAID 5/6)
  • 自愈(自动修复位翻转)
  • 许可不兼容 GPL,无法进入 Linux 内核主线
  • 通过 zfs-fuse / ZFSonLinux 使用

5. F2FS (Flash-Friendly FS)

  • 三星,2012
  • 专为 NAND Flash 设计
  • 移动端常用 (Android 内部存储)
  • 日志结构
  • 写入优化

6. NTFS

  • Windows 默认
  • Linux 通过 ntfs-3g 读写
  • 大文件
  • 权限、加密、压缩

7. FAT32 / exFAT

  • FAT32: 兼容性最好,但单文件 4 GB 上限
  • exFAT: 大文件,微软专利
  • U 盘/SD 卡常用

8. 临时文件系统

类型 用途
tmpfs 内存文件系统
ramfs 纯内存(无大小限制)
devtmpfs /dev 设备文件
proc /proc 进程信息
sysfs /sys 内核对象

9. 网络文件系统

FS 用途
NFS Linux/Unix 间共享
CIFS/SMB Windows 共享
9p virtio 共享文件夹
CephFS 分布式对象/块/文件
GlusterFS 分布式
sshfs SSH 挂载远程目录

十三、文件缓存 (Page Cache)

1. Page Cache 概述

Page Cache:内核管理的磁盘数据缓存,把磁盘块缓存在内存中

  • 读:先查 cache,再读盘
  • 写:先写 cache,延迟写盘
  • 极大提升 I/O 性能

2. Page Cache 关键参数

# /proc/sys/vm/
/proc/sys/vm/dirty_ratio              # 20% 内存时强制写盘
/proc/sys/vm/dirty_background_ratio   # 10% 内存时开始后台写
/proc/sys/vm/dirty_expire_centisecs   # 30 秒后过期
/proc/sys/vm/dirty_writeback_centisecs # 5 秒唤醒后台写线程

# 强制刷盘
sync
echo 1 > /proc/sys/vm/drop_caches     # 释放 page cache
echo 2 > /proc/sys/vm/drop_caches     # 释放 dentry + inode
echo 3 > /proc/sys/vm/drop_caches     # 全部释放

3. 缓存类型

  • Page Cache:文件数据缓存
  • Buffer Cache:块设备缓存(已合并到 Page Cache)
  • Dentry Cache:目录项缓存
  • Inode Cache:inode 缓存

4. 写策略

策略 说明
Write Through 写时同步到磁盘
Write Back 写时只写 cache,延迟刷盘
Write Around 写绕过 cache,直接落盘

十四、文件系统挂载 (Mount)

1. mount 命令

# 挂载
mount /dev/sda1 /mnt
mount -t ext4 /dev/sda1 /mnt
mount -o ro,noexec /dev/sda1 /mnt
mount -t nfs server:/path /mnt

# 卸载
umount /mnt
umount /dev/sda1

# 查看
mount
cat /proc/mounts
findmnt
df -h

2. 挂载选项

选项 说明
ro 只读
rw 读写
noexec 不允许执行
nosuid 忽略 SUID/SGID
nodev 不解释设备文件
sync 同步写
async 异步写
noatime 不更新访问时间(提速)
relatime 相对 atime(默认)
discard SSD TRIM

3. /etc/fstab

设备                挂载点  FS    选项             dump fsck
UUID=xxx-xxx       /       ext4  defaults         0    1
UUID=yyy-yyy       /home   ext4  defaults,noatime 0    2
UUID=zzz-zzz       swap    swap  defaults         0    0
UUID=www-www       /boot/efi vfat umask=0077      0    1
tmpfs               /tmp    tmpfs defaults,size=2G 0    0

4. 自动挂载

  • autofs:按需挂载
  • systemd 挂载单元:*.mount
  • udev:设备插入自动挂载
  • NetworkManager:网络共享自动挂载

十五、文件系统操作命令

1. 文件操作

# 创建
touch file
mkdir dir
mkdir -p a/b/c
ln file hardlink
ln -s target symlink

# 删除
rm file
rm -rf dir
unlink file

# 查看
ls -l
ls -la
ls -lh              # 人类可读大小
ls -lt              # 按时间
ls -lR dir          # 递归
ls -i               # 显示 inode

# 复制移动
cp src dst
cp -r dir/ newdir/
mv src dst

2. 文件内容查看

cat file
less file
more file
head -n 10 file
tail -n 10 file
tail -f file          # 实时跟踪
xxd file | head       # 十六进制
od -c file            # 八进制
file file             # 探测类型

3. 文件查找

# find
find / -name "*.txt"
find / -size +100M
find / -user root
find / -mtime -7

# locate (mlocate 索引)
locate filename
updatedb              # 更新索引

# whereis / which
whereis ls
which python

4. 文件比较

diff file1 file2
diff -u file1 file2
cmp file1 file2
md5sum file
sha256sum file

5. 文件压缩解压

# tar
tar -cvf a.tar dir/         # 打包
tar -xvf a.tar             # 解包
tar -czvf a.tar.gz dir/    # gzip
tar -cjvfa a.tar.bz2 dir/  # bzip2
tar -xzvf a.tar.gz         # 解压 gzip
tar -xjvf a.tar.bz2        # 解压 bzip2

# 通用
gzip file
gunzip file.gz
bzip2 file
xz file
zip a.zip file/
unzip a.zip

十六、文件系统修复 (fsck)

1. fsck 家族

fsck /dev/sda1           # 自动检测类型
fsck.ext4 /dev/sda1      # Ext 系列
e2fsck -fy /dev/sda1     # 强制修复 + 自动 yes
xfs_repair /dev/sda1     # XFS
btrfs check /dev/sda1    # Btrfs
reiserfsck /dev/sda1     # ReiserFS

2. 注意事项

  • 必须先卸载(根分区用 initramfs)
  • 备份数据
  • 不要在挂载的 FS 上跑 fsck

十七、文件系统加密

1. 块级加密 (全盘加密)

  • LUKS (Linux Unified Key Setup):标准
  • dm-crypt 内核驱动
# 加密
cryptsetup luksFormat /dev/sda1
cryptsetup open /dev/sda1 my_crypt
mkfs.ext4 /dev/mapper/my_crypt

# 挂载
mount /dev/mapper/my_crypt /mnt

2. 文件级加密

  • eCryptfs(Ubuntu 默认)
  • fscrypt(Linux 5.x 内核)
  • EncFS

十八、文件系统的关键概念

1. 原子操作

  • rename():原子操作
  • 多文件操作不是原子(用事务日志)

2. 文件系统事件

  • inotify:文件变化通知
  • fanotify:文件系统级通知
  • 应用:文件监控、自动同步

3. 配额 (Quota)

# 启用
mount -o remount,usrquota,grpquota /home
quotacheck -cug /home
quotaon /home
edquota -u user       # 设置用户配额

4. ACL (访问控制列表)

# 查看
getfacl file
# 设置
setfacl -m u:user:rw file
setfacl -m g:group:r file
# 移除
setfacl -x u:user file
setfacl -b file          # 全部移除

十九、日志与一致性

1. 文件系统日志 (Journal)

目的:防止崩溃后文件系统损坏

3 种日志方式:

方式 说明 速度
Writeback 只日志元数据,数据不日志 最快
Ordered 数据写完再日志元数据(默认)
Journal 元数据和数据都日志

流程:

1. 写入操作到来
2. 写入日志(Journal):"准备修改 X"
3. 写入实际数据
4. 写入日志:"修改完成"
5. 标记日志事务完成
6. 定期清理日志

崩溃时:
- 日志中有"准备"无"完成" → 回滚
- 日志中有"完成" → 重做

2. CoW (Copy on Write) 文件系统

  • Btrfs / ZFS
  • 写入时不修改原数据
  • 修改后形成新数据,旧数据保留
  • 天然防崩溃

二十、Linux 文件系统性能

1. 性能指标

  • IOPS (每秒 I/O 数)
  • 吞吐量 (MB/s)
  • 延迟 (μs)
  • 顺序 / 随机
  • 读 / 写比

2. 性能优化

# 文件系统挂载选项
mount -o noatime,nodiratime,data=writeback /dev/sda1 /mnt

# 文件系统参数
tune2fs -o journal_data_writeback /dev/sda1
tune2fs -L "DATA" /dev/sda1
tune2fs -m 1 /dev/sda1   # 保留空间 1%
tune2fs -r 0 /dev/sda1   # 不保留

# SSD 优化
mount -o discard /dev/sda1 /mnt
fstrim /mnt             # 定期 TRIM

3. 文件系统选择

场景 推荐 理由
通用 Ext4 稳定、兼容
大文件、数据库 XFS 性能好
容器/快照 Btrfs 快照、子卷
移动端 F2FS 闪存优化
跨平台 NTFS/exFAT 兼容性
U 盘 FAT32/exFAT 兼容

二十一、核心要点速记

  • inode = 文件的元数据 + 数据位置
  • 文件名存放在目录中,不是 inode
  • 硬链接 = 同一 inode,不能跨 FS
  • 软链接 = 独立文件,内容是路径
  • fd 0/1/2 = stdin/stdout/stderr
  • VFS = 统一 FS 抽象层
  • Page Cache = 磁盘缓存,写延迟刷盘
  • 日志 FS = Ext3/4/XFS,崩溃恢复
  • CoW FS = Btrfs/ZFS,写不修改原数据
  • LUKS = 块级加密
  • mmap = 高效的内存映射
  • find/locate/which = 查找
  • df -i = 看 inode 使用率
  • tune2fs = 调整 ext FS 参数
  • xfs_repair = 修复 XFS
  • du -sh = 看大小
  • sync = 强制刷盘
  • drop_caches = 释放缓存
  • chattr +i = 不可变文件
  • strace = 看进程的系统调用
  • lsof = 看进程打开的文件
  • fuser = 看使用文件的进程
  • FHS = Linux 文件系统标准结构
  • . .. ~ = 特殊目录
  • PATH = 可执行搜索路径
  • ext4 块大小 = 1/2/4 KB,默认 4 KB