用户传一个字符串
/usr/bin/ls,内核要一级一级查 dcache /
磁盘,穿越 mount 点、symlink,最终拿到目标 inode。这个过程叫
namei(name-to-inode),是 VFS
最频繁的操作。
一、先看图
flowchart LR
PATH["/usr/bin/ls"] --> SLASH[/ → root dentry]
SLASH --> USR[lookup "usr"]
USR --> BIN[lookup "bin"]
BIN -->|mount 穿越| BINMNT[/usr/bin 挂载点]
BINMNT --> LS[lookup "ls"]
LS --> INODE[inode 返回]
classDef step fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef mount fill:#a371f722,stroke:#a371f7,color:#adbac7;
class SLASH,USR,BIN,LS,INODE step
class BINMNT mount
二、三阶段查找
2.1 LOOKUP_RCU(快速路径)
不加任何锁,RCU 保护:
- 从根 dentry 开始
- 每个组件用
d_hash查 dcache - 验证
d_seq(dentry 序列号)保证数据一致 - mount 穿越用
mnt_seq验证
成功率 >99%(热路径命中 dcache)。
2.2 LOOKUP_LOCK(降级)
RCU 验证失败 → 降级拿 inode->i_rwsem +
dentry 引用。
2.3 LOOKUP_SLOW(磁盘)
dcache miss → 调用
inode->i_op->lookup() → 读磁盘目录。
三、mount 穿越
路径走到 mount 点时:
/ → root sb
/usr → root sb
/usr/bin → 如果 /usr 是独立挂载,穿越到新 sb 的 root dentry
follow_managed() 处理 automount + mount
穿越。
四、symlink 处理
遇到 symlink → 递归解析目标。
保护:
- 最大嵌套 40
层(
MAXSYMLINKS = 40) - 最大总长度 4096 字节
- 绝对 symlink 从根重新开始
五、openat2 与 RESOLVE_*
openat2(2) 引入安全控制:
struct open_how how = {
.flags = O_RDONLY,
.resolve = RESOLVE_NO_SYMLINKS | RESOLVE_BENEATH,
};
int fd = openat2(dirfd, "path", &how, sizeof(how));- RESOLVE_NO_SYMLINKS:不跟 symlink
- RESOLVE_BENEATH:不能越出 dirfd(防路径穿越)
- RESOLVE_IN_ROOT:把 dirfd 当 / 看(chroot 语义)
- RESOLVE_NO_XDEV:不穿越 mount 点
- RESOLVE_NO_MAGICLINKS:不跟 /proc/self/fd/N 这类魔法链接
容器安全的基石。
六、AT_EMPTY_PATH 与 fstat 优化
openat(dirfd, "", AT_EMPTY_PATH)
// 等价于对 dirfd 本身操作配合 /proc/self/fd/N →
可以用路径操作已打开的 fd。
七、case-insensitive
ext4 5.2+ 支持 casefold 特性(UTF-8 case
folding):
mkfs.ext4 -O casefold /dev/sda1
chattr +F /mnt/mydir # 该目录及子目录大小写不敏感内核在 lookup 时用 UTF-8 NFC 正规化 + case fold 比较。
限制:不同 locale 的 case mapping 不同;不支持 Turkic “i” 问题。
八、RENAME_EXCHANGE
renameat2(olddirfd, "a", newdirfd, "b", RENAME_EXCHANGE);
// 原子交换 a 和 b比 rename+rename 安全——不会出现中间状态。
RENAME_NOREPLACE:不覆盖目标(原子
create-or-fail)。
九、dcache 调优
# 当前 dcache 状态
cat /proc/sys/fs/dentry-state
# negative dentry 限制(6.1+)
sysctl fs.negative-dentry-limit=0 # 不限(默认)大量 negative dentry 场景:编译器频繁探测不存在的头文件 → dcache 膨胀。
十、路径查找性能
# 衡量 lookup 速度
perf stat -e 'syscalls:sys_enter_openat' -- find /usr -name '*.so' > /dev/null
# dcache 命中率
perf stat -e dentry-cache-misses,dentry-cache-accesses 2>/dev/null
# 或看 /proc/slabinfo dentry 变化十一、小结
- namei 走三阶段:RCU → 加锁 → 磁盘
- mount 穿越和 symlink 是两大复杂点
- openat2 RESOLVE_* 是容器安全的路径查找利器
- case-insensitive 在 ext4 上可选启用
参考文献
Documentation/filesystems/path-lookup.rst- Neil Brown, “Pathname lookup in Linux.” LWN.net (6 篇系列)
fs/namei.cinclude/uapi/linux/openat2.hDocumentation/filesystems/ext4/casefold.rst
工具
strace -e openatperf trace/proc/sys/fs/dentry-state
上一篇:VFS 四层抽象 下一篇:fd 表与 struct file
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内核内存调试
内核内存 bug 是最难追的:UAF、OOB、double free、leak 都可能沉默数月。本文讲 KASAN 三种模式、KFENCE 生产采样、kmemleak、SLUB_DEBUG、UBSAN/KCSAN 联动。
【操作系统百科】VFS 四层抽象
Linux 的一切皆文件靠 VFS 实现——superblock、inode、dentry、file 四层抽象加 ops 表。本文讲 VFS 核心数据结构、dcache、inode cache、RCU lookup,以及文件系统如何插入 VFS。
操作系统百科
Linux 6.x 视角下的操作系统系列索引:110 篇覆盖调度、虚拟内存、文件系统与 I/O、并发、隔离、可观测性,按主题、阅读路径与关键问题三种入口组织。
【操作系统百科】用户态分配器:jemalloc vs tcmalloc
jemalloc 与 tcmalloc 都想解决多线程分配器的老问题:锁争抢、碎片、RSS 膨胀与回收抖动。但两者把优化重点放在了不同位置:tcmalloc 更激进地把热路径推到 per-CPU,jemalloc 则把 arena、extent、decay 和 profiling 做成了一套更完整的内存治理工具箱。