文件系统: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 的核心概念。通过这篇文章,我们学习了:
核心概念
- 一切皆文件:Unix 的设计哲学
- VFS:统一多种文件系统的接口
- inode:文件的核心数据结构
- 权限模型:用户/组/其他的 rwx 权限
- 文件系统布局:超级块、位图、inode表、数据块
实践技能
- 使用
chmod、chown管理权限 - 理解并设置特殊权限(SUID、SGID、Sticky Bit)
- 使用
mount、umount管理文件系统 - 创建硬链接和符号链接
- 高效查找和管理文件
下一步
在后续文章中,我们将学习:
- 设备驱动:字符设备和块设备
- 网络编程:TCP/IP 和 Socket
- 系统调用:深入内核接口
“In Unix, everything is a file. If it’s not a file, it’s a process.” — Unix Philosophy
理解文件系统是理解 Linux 的关键一步。继续探索,你会发现更多精彩!
← 返回系列概述: Linux 概述:从 Unix 到开源革命
参考资源: