首页
/ SerenityOS stat 命令完全指南:深入解析文件状态查询工具的实现与用法

SerenityOS stat 命令完全指南:深入解析文件状态查询工具的实现与用法

2026-09-09 18:58:59作者:裘晴惠Vivianne

导读

stat 是 SerenityOS 用户态中的一款基础文件查询工具,用于显示指定文件的详细状态信息,包括 inode 编号、文件大小、链接数、属主/属组、权限模式以及访问/修改/变更时间。本文以 SerenityOS 官方手册 Base/usr/share/man/man1/stat.md 为骨架,结合其源码实现 Userland/Utilities/stat.cpp、内核系统调用 Kernel/Syscalls/stat.cpp 与相关测试,带你同时掌握 stat 的实战用法与底层原理,读完即可熟练诊断文件类型、权限与时间戳信息。

命令概览:Synopsis 与核心用途

手册给出的命令形态为:

$ stat [-L] file

其核心语义是"Display file status"——显示文件状态,并提供关于该文件的更多信息。与 ls -l 只展示目录条目不同,stat 直接针对给定的路径查询文件系统的元数据(metadata),并以结构化、人类可读的方式输出,是排查文件权限问题、确认符号链接指向、核对时间戳的得力工具。

从源码看,stat 实际支持多个文件参数(位置参数被定义为 Vector<StringView> files),因此一次可以查询多个目标:

args_parser.add_positional_argument(files, "File(s) to stat", "file", Core::ArgsParser::Required::Yes);

也就是说,stat file1 file2 的用法在实现层面完全合法,手册仅展示了最简形式。

选项详解:-L、--help 与 --version

手册列出的选项如下:

  • -L:Follow links to files(跟随符号链接,查询链接指向的目标文件)
  • --help:显示帮助信息并退出
  • --version:打印版本信息

--help--version 是 SerenityOS 命令行工具的通用约定,由参数解析库 Userland/Libraries/LibCore/ArgsParser.h 统一提供,几乎所有 Userland 工具(如 lsdufile)都遵循这一约定。

-L 的底层语义:stat 与 lstat 的分野

-L 选项是理解 stat 的关键。在源码 Userland/Utilities/stat.cpp 中,选项被解析后直接决定调用哪个系统调用封装:

auto st = TRY(should_follow_links ? Core::System::stat(file) : Core::System::lstat(file));
  • 不带 -L:调用 lstat,查询符号链接本身的元数据(链接文件自身的 inode、大小、类型为 l);
  • -L:调用 stat,跟随符号链接,返回链接最终指向目标文件的元数据。

这两个封装定义于 Userland/Libraries/LibCore/System.h

ErrorOr<struct stat> stat(StringView path);
ErrorOr<struct stat> lstat(StringView path);

默认不跟随链接的原因

默认不跟随符号链接(即默认 lstat 行为)是 POSIX 体系的常见设计,目的是让用户能精确检查链接本身(例如确认链接是否悬空、链接自身的 inode 与时间戳),避免在链接指向不存在或不可访问的目标时误报错误。这也正是手册示例中使用 stat -L /bin/ls 的原因——/bin/ls 在 SerenityOS 中通常是符号链接,加 -L 才能看到最终可执行文件的真实状态。

输出字段逐项解析

手册给出的示例输出(stat -L /bin/ls):

$ stat -L /bin/ls
    File: /bin/ls
   Inode: 8288
    Size: 184896
   Links: 1
  Blocks: 376
     UID: 0 (root)
     GID: 0 (root)
    Mode: (100755/-rwxr-xr-x)
Accessed: 2022-10-20 03:56:56
Modified: 2022-10-20 03:55:34
 Changed: 2022-10-20 03:56:57

结合 Userland/Utilities/stat.cpp 的打印逻辑,各字段含义如下:

字段 输出示例 对应 struct stat 成员 含义
File /bin/ls 传入的路径名
Device 见下文 st_dev 文件所在设备的设备号
Inode 8288 st_ino inode 编号,同一文件系统内唯一标识文件
Size 184896 st_size 文件字节大小;对字符/块设备则替换为设备号输出
Links 1 st_nlink 硬链接数量
Blocks 376 st_blocks 文件占用的磁盘块数
UID 0 (root) st_uid 属主用户 ID 及用户名
GID 0 (root) st_gid 属主组 ID 及组名
Mode (100755/-rwxr-xr-x) st_mode 文件类型 + 权限位的数字与符号双重表示
Accessed 2022-10-20 03:56:56 st_atim 最后访问时间
Modified 2022-10-20 03:55:34 st_mtim 最后修改时间(内容变更)
Changed 2022-10-20 03:56:57 st_ctim 状态变更时间(元数据变更,如 chmod/chown)

时间戳的特殊处理:纳秒精度

AccessedModifiedChanged 三个时间戳在实现上并非简单地打印秒数,而是由 lambda 输出带纳秒部分的时间(Userland/Utilities/stat.cpp):

auto print_time = [](timespec t) {
    outln("{}.{:09}", Core::DateTime::from_timestamp(t.tv_sec).to_byte_string(), t.tv_nsec);
};

可见其使用 timespec 结构,时间字符串后接 . 与 9 位纳秒补零输出。示例输出中的时间之所以未显示纳秒,是因为手册记录时省略了后续精度字段;实际运行时(如对新建文件执行 stat)会看到类似 2026-09-09 03:32:23.123456789 的完整格式。这也侧面印证了 SerenityOS 文件系统对高精度时间戳的完整支持。

权限模式的符号渲染逻辑

Mode 字段是输出中最"精密"的部分,源码在 Userland/Utilities/stat.cpp 中手工渲染:

  1. 先以八进制打印完整 st_mode(如 100755,其中前导 1 表示普通文件类型位);
  2. 再根据 S_ISDIR / S_ISLNK / S_ISBLK / S_ISCHR / S_ISFIFO / S_ISSOCK / S_ISREG 宏判定类型字符:d(目录)、l(符号链接)、b(块设备)、c(字符设备)、f(FIFO)、s(套接字)、-(普通文件)、?(未知);
  3. 随后逐位输出 9 个权限字符:读 r、写 w;执行位遇 S_ISUID/S_ISGID 时显示 s(setuid/setgid),否则显示 x-;末位遇 S_ISVTX(粘滞位)显示 t,否则显示 x-

因此 -rwxr-xr-x 表示"普通文件、属主可读写执行、属组可读执行、其他可读执行",与 ls -l 的显示约定一致。

实战:常见使用场景

1. 查看文件详细信息

$ stat /etc/passwd
    File: /etc/passwd
   Inode: 36869
    Size: 1412
   Links: 1
  Blocks: 8
     UID: 0 (root)
     GID: 0 (root)
    Mode: (100644/-rw-r--r--)
Accessed: 2026-09-09 02:10:11.000000000
Modified: 2026-09-09 02:10:11.000000000
 Changed: 2026-09-09 02:10:11.000000000

2. 检查符号链接本身 vs 其目标

$ stat /bin/sh          # 查看链接本身(类型为 l,大小较小)
$ stat -L /bin/sh       # 查看链接指向的实际 shell(类型为 -,大小较大)

对比两次输出的 Mode 首字符(l vs -)与 Size,即可快速判断链接的指向关系。

3. 一次性查询多个文件

$ stat file1.txt file2.txt

每个文件依次输出一组字段块,任一文件出错(如不存在)时,程序会继续处理后续文件,并在最后以非零退出码结束——见 Userland/Utilities/stat.cpp 的错误处理循环:

bool had_error = false;
for (auto& file : files) {
    auto r = stat(file, should_follow_links);
    if (r.is_error()) {
        had_error = true;
        warnln("stat: cannot stat '{}': {}", file, strerror(r.error().code()));
    }
}
return had_error;

对不存在的路径,stderr 会输出 stat: cannot stat 'xxx': No such file or directory 之类的诊断信息。

4. 查看设备文件的设备号

对字符设备或块设备,stat 不打印 Size,而是打印主/次设备号(Userland/Utilities/stat.cpp):

if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
    outln("  Device: {},{}", major(st.st_rdev), minor(st.st_rdev));
else
    outln("    Size: {}", st.st_size);

例如 stat /dev/tty 会输出类似 Device: 4,0 的主次设备号,同时 Devicest_dev)字段仍保留文件所在设备的编号。

源码级原理:stat 的完整调用链

用户态:pledge 与参数解析

stat 的入口是 serenity_mainUserland/Utilities/stat.cpp),首先执行系统调用承诺:

TRY(Core::System::pledge("stdio rpath"));

pledge 声明该进程只需要 stdio(标准 I/O)与 rpath(只读路径访问)两类权限,这是 SerenityOS 的安全机制——即使程序被攻破,也无法执行写入类系统调用。

随后用 Core::ArgsParser 解析 -L 与位置参数,再逐文件调用内部的 stat() 函数。

内核态:sysstatsysstat 与 sysfstat

用户态的 Core::System::stat/lstat 最终陷入内核系统调用。内核实现在 Kernel/Syscalls/stat.cpp

ErrorOr<FlatPtr> Process::sys$stat(Userspace<Syscall::SC_stat_params const*> user_params)
{
    TRY(require_promise(Pledge::rpath));
    auto params = TRY(copy_typed_from_user(user_params));
    auto path = TRY(get_syscall_path_argument(params.path));

    CustodyBase base(params.dirfd, path->view());
    auto metadata = TRY(VirtualFileSystem::lookup_metadata(
        vfs_root_context(), credentials(), path->view(), base,
        params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
    auto statbuf = TRY(metadata.stat());
    TRY(copy_to_user(params.statbuf, &statbuf));
    return 0;
}

关键点:

  • 权限检查:内核要求进程持有 rpath pledge,与用户态 pledge("stdio rpath") 一一对应;
  • 符号链接策略follow_symlinks 参数为真时传 0,否则传 O_NOFOLLOW_NOERROR——这就是 -L 在内核层面的最终落点;
  • 元数据获取:通过 VirtualFileSystem::lookup_metadata 在虚拟文件系统层查找路径对应的 inode 元数据,再由 metadata.stat() 填充 struct stat
  • 跨进程拷贝:用 copy_to_user 把内核态 statbuf 安全地写回用户态缓冲区。

同文件还实现了基于文件描述符的 sys$fstatKernel/Syscalls/stat.cpp),它从 open_file_description(fd) 直接取描述符对应的文件元数据,不涉及路径解析——这也是 fstat 不受符号链接影响、且可用于已打开文件的原因。

测试佐证

内核文件权限测试 Tests/Kernel/TestKernelFilePermissions.cppTests/Kernel/TestKernelFilePermissions.cpp 分别用 fstat 验证打开文件后的元数据读取、用 lstat 验证符号链接自身的状态查询,与 stat 工具内部的 lstat/stat 选择逻辑互相印证,可作为理解系统调用语义的补充材料。

在 SerenityOS 中查看本文对应手册

SerenityOS 内置 man 页面查看器,运行:

$ man stat

即可在终端内阅读与 Base/usr/share/man/man1/stat.md 同源的格式化手册页(含 Name、Synopsis、Description、Options、Examples 五节)。同时,该手册文件也随系统镜像分发到 /usr/share/man/man1/stat.md,其他命令的同类手册位于同一目录(如 man1/ls.mdman1/du.md 等)。

小结

stat 虽然是一个"小工具",但它串联了 SerenityOS 的多层设计:ArgsParser 的参数解析、pledge 权限模型、lstat/stat 的符号链接语义、虚拟文件系统层的元数据查询,以及 struct stat 中 inode、权限位、纳秒时间戳的完整表达。掌握它,等于同时理解了一条 SerenityOS 用户态→内核态的文件元数据查询链路——这份能力在调试权限问题、诊断挂载设备、排查时间戳异常时都能直接复用。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525