Featured image of post 文件系统:Linux 的数据宫殿

文件系统:Linux 的数据宫殿

深入理解 Linux 虚拟文件系统(VFS)、inode、文件权限、Ext4 原理以及文件系统操作

文件系统:Linux 的数据宫殿

引言

文件系统是操作系统中最重要的组成部分之一。在 Linux 中,一切皆文件——普通文件、目录、设备、套接字等都通过文件系统接口访问。在这篇文章中,我们将深入探讨 Linux 文件系统的奥秘。

一切皆文件

Unix 哲学

Linux 继承了 Unix 的设计哲学:一切皆文件

  mindmap
  root((一切皆文件))
    普通文件
      文本文件
      二进制文件
      数据文件
    目录
      文件夹
      路径
    设备文件
      块设备/dev/sda
      字符设备/dev/tty
    进程文件
      /proc
    网络文件
      /dev/tcp
      Socket
    其他
      管道
      符号链接

文件类型

    # 查看文件类型
ls -la /etc/passwd /dev/sda /dev/tty /bin/bash

# 输出示例:
# -rw-r--r--  1 root root  2941 Feb  10 10:00 /etc/passwd
# brw-rw----  1 root disk   8, 0 Feb  10 10:00 /dev/sda
# crw-rw-rw-  1 root tty   5, 0 Feb  10 10:00 /dev/tty
# -rwxr-xr-x  1 root root  1.2M Feb  10 10:00 /bin/bash

# 第一个字符表示文件类型:
# - : 普通文件
# d : 目录
# b : 块设备(块设备驱动)
# c : 字符设备(字符设备驱动)
# l : 符号链接
# p : 命名管道(FIFO)
# s : 套接字
  

虚拟文件系统(VFS)

VFS 的作用

VFS 提供了一个统一的文件系统接口,使得 Linux 可以支持多种文件系统:

  flowchart TB
    A[用户程序] -->|open, read, write| B[VFS 层]

    B --> C[Ext4]
    B --> D[XFS]
    B --> E[Btrfs]
    B --> F[NFS]
    B --> G[ProcFS]
    B --> H[SysFS]

    C --> I[块设备层]
    D --> I
    E --> I

    F --> J[网络层]

    G --> K[内核数据结构]
    H --> K

    style B fill:#fff3e0
    style C fill:#e3f2fd
    style D fill:#e3f2fd
    style E fill:#e3f2fd

VFS 的四大对象

1. 超级块(super_block)

超级块表示整个文件系统:

    struct super_block {
    struct list_head s_list;      // 所有超级块的链表
    dev_t s_dev;                  // 设备标识符
    unsigned long s_magic;        // 文件系统魔数

    const struct super_operations *s_op;
    struct file_system_type *s_type;

    struct inode *s_root;         // 根目录 inode
    // ...
};
  
    # 查看文件系统超级块信息
tune2fs -l /dev/sda1

# 查看文件系统魔数
blkid /dev/sda1
  

2. 索引节点(inode)

inode 存储文件的元数据(不包含文件名):

    struct inode {
    umode_t i_mode;               // 文件类型和权限
    uid_t i_uid;                  // 所有者 UID
    gid_t i_gid;                  // 所属组 GID
    loff_t i_size;                // 文件大小(字节)
    struct timespec i_atime;      // 最后访问时间
    struct timespec i_mtime;      // 最后修改时间
    struct timespec i_ctime;      // 最后状态改变时间

    struct inode_operations *i_op;
    struct file_operations *i_fop;

    struct address_space *i_mapping;
    // ...
};
  

inode 包含的信息

  • 文件类型
  • 权限
  • 所有者
  • 文件大小
  • 时间戳
  • 数据块位置指针

inode 不包含

  • 文件名(文件名存储在目录项中)
    # 查看 inode 编号
ls -i file.txt

# 查看文件 inode 信息
stat file.txt

# 输出示例:
#   File: file.txt
#   Size: 1234       Blocks: 8         IO Block: 4096   regular file
# Access: (0644/-rw-r--r--)  Uid: ( 1000/   user)   Gid: ( 1000/   user)
# Access: 2026-01-11 00:00:00.000000000 +0800
# Modify: 2026-01-10 23:59:59.000000000 +0800
# Change: 2026-01-10 23:59:59.000000000 +0800
# Birth: 2026-01-10 00:00:00.000000000 +0800
  

3. 目录项(dentry)

dentry 将路径名连接到 inode:

    struct dentry {
    struct dentry *d_parent;      // 父目录
    struct qstr d_name;           // 文件名
    struct inode *d_inode;        // 关联的 inode

    unsigned int d_flags;         // 目录项标志
    struct dentry_operations *d_op;
    // ...
};
  

dentry 缓存(dcache)

  • 加速路径查找
  • 最近使用的目录项保持在内存中
    # 清理 dentry 缓存
sync && echo 3 > /proc/sys/vm/drop_caches
  

4. 文件(file)

file 表示打开的文件:

    struct file {
    struct path f_path;
    struct inode *f_inode;        // 关联的 inode
    const struct file_operations *f_op;

    loff_t f_pos;                 // 当前读写位置
    struct fown_struct f_owner;
    // ...
};
  
    # 查看进程打开的文件
lsof -p 1234

# 查看某个文件被哪些进程打开
lsof /var/log/syslog

# /proc 文件系统也提供这些信息
ls -la /proc/1234/fd/
  

文件系统布局

Ext4 文件系统结构

    ┌────────────────────────────────────────────────┐
│              Boot Sector (引导扇区)            │
├────────────────────────────────────────────────┤
│              超级块(Superblock)               │
│  - 文件系统大小、块数、inode数                 │
│  - 每1024个块有一个超级块备份                  │
├────────────────────────────────────────────────┤
│              组描述符(Group Descriptors)      │
│  - 块位图、inode位图、inode表的位置            │
├────────────────────────────────────────────────┤
│  数据块组 1  │ 数据块组 2  │ ... │ 数据块组 N  │
│ ┌──────────┐ │ ┌──────────┐ │    │ ┌──────────┐│
│ │超级块备份│ │ │超级块备份│ │    │ │超级块备份││
│ ├──────────┤ │ ├──────────┤ │    │ ├──────────┤│
│ │块位图    │ │ │块位图    │ │    │ │块位图    ││
│ ├──────────┤ │ ├──────────┤ │    │ ├──────────┤│
│ │inode位图 │ │ │inode位图 │ │    │ │inode位图 ││
│ ├──────────┤ │ ├──────────┤ │    │ ├──────────┤│
│ │inode表   │ │ │inode表   │ │    │ │inode表   ││
│ ├──────────┤ │ ├──────────┤ │    │ ├──────────┤│
│ │数据块    │ │ │数据块    │ │    │ │数据块    ││
│ └──────────┘ │ └──────────┘ │    │ └──────────┘│
└────────────────────────────────────────────────┘
  

inode 结构详解

inode 包含指向数据块的指针:

    ┌────────────────────────────────────┐
│         inode 结构                 │
├────────────────────────────────────┤
│  文件元数据(大小、权限、时间等)  │
├────────────────────────────────────┤
│  直接指针 (12个)                   │
│    - 指向数据块 0-11               │
├────────────────────────────────────┤
│  单级间接指针                      │
│    - 指向一个索引块                │
│    - 该索引块包含数据块指针        │
├────────────────────────────────────┤
│  双级间接指针                      │
│    - 指向一个索引块                │
│    - 该索引块指向二级索引块        │
│    - 二级索引块包含数据块指针      │
├────────────────────────────────────┤
│  三级间接指针                      │
│    - 类似双级,但多一层            │
└────────────────────────────────────┘

文件大小计算(假设块大小为 4KB):
- 直接指针:12 × 4KB = 48KB
- 单级间接:256 × 4KB = 1MB
- 双级间接:256 × 256 × 4KB = 256MB
- 三级间接:256³ × 4KB = 64GB
  

文件权限

权限模型

Linux 使用传统的 Unix 权限模型:

    -rw-r--r--  1 user group 1234 Jan 10 10:00 file.txt
││││││││││
│││││││││└─ 其他用户权限(r--)
│││││││└──── 组用户权限(r--)
│││││└─────── 所有者权限(rw-)
│││└───────── 文件类型(- = 普通文件)
│└────────── 特殊权限位
└─────────── ACL 或 SELinux 上下文
  

权限类型

  • r(read):读取
  • w(write):写入
  • x(execute):执行

权限对象

  • 所有者(user):文件创建者
  • 组(group):文件所属组
  • 其他(others):其他所有用户

权限表示

符号模式

    # 查看权限
ls -l file.txt

# 修改权限
chmod u+x file.txt      # 所有者添加执行权限
chmod go-w file.txt     # 组和其他移除写入权限
chmod a=r file.txt      # 所有用户设置为只读

# 常用组合
chmod 755 script.sh     # rwxr-xr-x
chmod 644 file.txt      # rw-r--r--
chmod 600 secret.key    # rw-------(私密文件)
  

数字模式

    # 权限位计算
r = 4
w = 2
x = 1
- = 0

# 示例
rwxr-xr-- = 754
│││││││││
││││││││└─ 其他:4 (r--)
││││││└──── 组:5 (r-x)
││││└─────── 所有者:7 (rwx)

# 常用权限值
644 (rw-r--r--)  # 普通文件
755 (rwxr-xr-x)  # 可执行文件/目录
600 (rw-------)  # 私密文件
640 (rw-r-----)  # 组共享文件
  

目录权限

    # 目录权限含义
r (read)    # 可以列出目录内容
w (write)   # 可以创建/删除文件(需要 x 权限)
x (execute) # 可以进入目录(cd)

# 示例
drwxr-xr-x  2 user group 4096 Jan 10 10:00 documents
│││││││││
││││││││└─ 其他:r-x(可以进入,但不能写入)
││││││└──── 组:r-x(可以进入,但不能写入)
││││└─────── 所有者:rwx(完全控制)
  

删除文件需要父目录的写权限,而不是文件本身的权限!

特殊权限

SUID(Set User ID)

    # 以文件所有者身份运行
chmod u+s /usr/bin/passwd
# 或
chmod 4755 /usr/bin/passwd

# 示例:passwd 命令
-rwsr-xr-x  1 root root  68K Jan 10 10:00 /usr/bin/passwd
#           ^
#           s 代替了 x,表示设置了 SUID

# 作用:普通用户运行 passwd 时,临时获得 root 权限
# 从而可以修改 /etc/shadow 文件
  

SGID(Set Group ID)

    # 在目录中创建的文件继承目录的组
chmod g+s /shared/project
# 或
chmod 2755 /shared/project

# 示例
drwxrwsr-x  2 user project 4096 Jan 10 10:00 /shared/project
#           ^
#           s 表示设置了 SGID
  

Sticky Bit

    # 只有文件所有者才能删除文件
chmod +t /tmp
# 或
chmod 1777 /tmp

# 示例
drwxrwxrwt  10 root root 4096 Jan 10 10:00 /tmp
#            ^
#            t 表示设置了 sticky bit

# 作用:在 /tmp 中,用户只能删除自己的文件
  

默认权限(umask)

    # 查看当前 umask
umask
# 输出:0002

# umask 从权限中"减去"这些位
# 文件默认权限:666 - umask = 664 (rw-rw-r--)
# 目录默认权限:777 - umask = 775 (rwxrwxr-x)

# 设置 umask
umask 022              # 新文件权限 644 (rw-r--r--)
umask 077              # 新文件权限 600 (rw-------)

# 永久设置(在 ~/.bashrc 中)
echo 'umask 027' >> ~/.bashrc
  

文件操作命令

基础操作

    # 创建文件
touch file.txt                 # 创建空文件
echo "Hello" > file.txt        # 创建并写入内容
cat > file.txt << EOF         # 多行输入
Line 1
Line 2
EOF

# 复制文件
cp file1.txt file2.txt         # 复制
cp -r dir1 dir2                # 递归复制目录
cp -p file.txt backup.txt      # 保留属性
cp -i file.txt target.txt      # 覆盖前确认

# 移动/重命名
mv old.txt new.txt             # 重命名
mv file.txt /tmp/              # 移动

# 删除文件
rm file.txt                    # 删除
rm -r dir/                     # 递归删除目录
rm -i file.txt                 # 确认删除
rm -f file.txt                 # 强制删除
  

查找文件

    # find:按条件查找
find /home -name "*.txt"                    # 按名称查找
find /home -type f -name "*.conf"          # 查找文件
find /home -type d -name "log"             # 查找目录
find /home -size +100M                     # 查找大于100MB的文件
find /home -mtime -7                       # 查找7天内修改的文件
find /home -user john                      # 查找john拥有的文件
find /home -perm 777                       # 查找777权限的文件

# locate:快速查找(使用数据库)
locate nginx.conf
sudo updatedb                              # 更新数据库

# which:查找命令位置
which python3
whereis python3                            # 查找二进制、源码、手册页
  

文件内容查看

    # 查看文件
cat file.txt                  # 显示全部
less file.txt                 # 分页查看(推荐)
more file.txt                 # 简单分页

# 查看部分
head -n 10 file.txt           # 前10行
tail -n 10 file.txt           # 后10行
tail -f /var/log/syslog       # 实时查看日志

# 查看特定行
sed -n '10,20p' file.txt      # 第10-20行

# 查找内容
grep "error" file.txt
grep -r "TODO" ./             # 递归搜索目录
grep -i "hello" file.txt      # 忽略大小写
grep -n "pattern" file.txt    # 显示行号
grep -v "comment" file.txt    # 反向匹配
  

文件系统操作

挂载(mount)

    # 查看挂载情况
mount                          # 列出所有挂载点
df -h                          # 显示磁盘使用情况

# 挂载文件系统
sudo mount /dev/sdb1 /mnt/data
sudo mount -t ext4 /dev/sdb1 /mnt/data

# 挂载 ISO 镜像
sudo mount -o loop image.iso /mnt

# 挂载 NFS
sudo mount -t nfs server:/share /mnt/nfs

# 卸载
sudo umount /mnt/data

# /etc/fstab:开机自动挂载
# <设备>      <挂载点>   <类型>  <选项>              <dump> <pass>
UUID=xxx    /           ext4    errors=remount-ro   0      1
/dev/sdb1   /mnt/data   ext4    defaults           0      2
  

磁盘空间管理

    # 查看磁盘使用
df -h                          # 人类可读格式
df -i                          # 查看 inode 使用情况

# 查看目录大小
du -sh /home/user              # 目录总大小
du -h --max-depth=1 /home/user  # 一级子目录大小
du -sh * | sort -hr             # 按大小排序

# 查找大文件
find /home -type f -size +100M -exec ls -lh {} \;

# 清理空间
sudo apt clean                 # 清理 apt 缓存
sudo journalctl --vacuum-time=7d  # 保留7天日志
  

符号链接和硬链接

硬链接

    # 创建硬链接
ln original.txt link.txt

# 特点:
# 1. 指向同一个 inode
# 2. 同一个文件的不同名字
# 3. 不能跨文件系统
# 4. 不能链接目录
# 5. 删除原文件,链接仍然有效

# 查看
ls -li original.txt link.txt
# 123456 -rw-r--r-- 2 user user ... original.txt
# 123456 -rw-r--r-- 2 user user ... link.txt
#        ↑                  ↑
#      相同 inode        硬链接计数
  

符号链接(软链接)

    # 创建符号链接
ln -s /path/to/original symlink

# 特点:
# 1. 指向路径,不是 inode
# 2. 有自己的 inode
# 3. 可以跨文件系统
# 4. 可以链接目录
# 5. 删除原文件,链接失效

# 查看
ls -la symlink
# lrwxrwxrwx 1 user user 13 Jan 10 10:00 symlink -> /path/to/original
#          ↑
#      l 表示符号链接
  

实战技巧

批量重命名

    # 使用 rename 命令
rename 's/old/new/' *.txt      # 将所有 old 替换为 new
rename 'y/a-z/A-Z/' *.txt      # 转换为大写

# 使用 bash 循环
for f in *.jpg; do
    mv "$f" "prefix_${f}"
done
  

查找并删除

    # 查找并删除空文件
find . -type f -empty -delete

# 查找并删除旧文件
find /tmp -type f -mtime +30 -delete

# 查找并删除大于100MB的文件
find . -type f -size +100M -exec rm {} \;
  

文件备份

    # 创建带时间戳的备份
cp file.txt file_$(date +%Y%m%d_%H%M%S).txt

# 使用 rsync 同步
rsync -av --progress /src/ /dst/

# 保留权限、时间戳、所有者
cp -p source.txt backup.txt
  

总结

文件系统是 Linux 的核心概念。通过这篇文章,我们学习了:

核心概念

  1. 一切皆文件:Unix 的设计哲学
  2. VFS:统一多种文件系统的接口
  3. inode:文件的核心数据结构
  4. 权限模型:用户/组/其他的 rwx 权限
  5. 文件系统布局:超级块、位图、inode表、数据块

实践技能

  • 使用 chmodchown 管理权限
  • 理解并设置特殊权限(SUID、SGID、Sticky Bit)
  • 使用 mountumount 管理文件系统
  • 创建硬链接和符号链接
  • 高效查找和管理文件

下一步

在后续文章中,我们将学习:

  • 设备驱动:字符设备和块设备
  • 网络编程:TCP/IP 和 Socket
  • 系统调用:深入内核接口

“In Unix, everything is a file. If it’s not a file, it’s a process.” — Unix Philosophy

理解文件系统是理解 Linux 的关键一步。继续探索,你会发现更多精彩!


← 返回系列概述: Linux 概述:从 Unix 到开源革命


参考资源

A winner is just a loser who tried one more time.
Robust AI
使用 Hugo 构建
主题 StackJimmy 设计