FEATURED · 精选文章

Serenity OS lsdir:基于 get_dir_entries 系统调用的底层目录项枚举工具

发布时间 / 2026/9/10 11:57:38
来源 / 创域科博编辑部
栏目 / 资讯中心
Serenity OS lsdir:基于 get_dir_entries 系统调用的底层目录项枚举工具 Serenity OS lsdir基于 get_dir_entries 系统调用的底层目录项枚举工具【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity本文介绍 Serenity OS 用户态实用工具lsdir它通过LibCore::DirIterator直接读取目录项dirent的原始字段输出每个条目的名称、POSIXDT_*或人类可读的文件类型、以及 inode 号。读完本篇你将掌握lsdir的完整命令语法与选项行为并理解其“仅依赖get_dir_entries系统调用、不做逐条fstat”的实现原理。1. 工具定位列出目录项而非文件信息lsdir的手册页位于 listdir.md其定义为lsdir - list directory entries用法概要# lsdir [options...] [path...]与常见的ls不同lsdir关注的不是文件属性大小、权限、时间戳而是目录项本身最底层的三要素条目名name、文件类型type、inode 号inode number。文件类型既可以用 POSIXDT_*常量格式输出也可以用人类可读的名称输出。手册页明确指出其实现路径The utility usesLibCoreDirIteratorobject and restrict its functionality to theget_dir_entriessyscall only, to get the raw values of each directory entry.也就是说它刻意把功能收敛到一次get_dir_entries系统调用上拿到的是 dirent 结构中的“原始值”raw values而不是通过stat二次推断出来的结果。这一点在下面第 4 节的源码分析中会展开。2. 选项与参数详解手册页定义了 2 个选项和 1 个位置参数对应源码 listdir.cpp 中的注册代码选项长选项名默认值作用-P--posix-names关闭以 POSIX 常量名如DT_REG、DT_DIR显示文件类型关闭时显示人类可读名称如File、Directory-t--total-entries-count关闭遍历完每个目录后打印该目录实际列出的条目总数path—位置参数可重复非必填.当前工作目录要列出的目录路径可一次传入多个源码中选项注册与默认值逻辑listdir.cpp#L22-L32VectorStringView paths; Core::ArgsParser args_parser; args_parser.set_general_help(List Dirent entries in a directory.); args_parser.add_option(flag_show_unix_posix_file_type, Show POSIX names for file types, posix-names, P); args_parser.add_option(flag_show_total_count, Show total count for each directory being iterated, total-entries-count, t); args_parser.add_positional_argument(paths, Directory to list, path, Core::ArgsParser::Required::No); args_parser.parse(arguments); if (paths.is_empty()) paths.append(.sv);两个值得注意的实现细节位置参数为Required::No不传路径时程序自动以.当前工作目录作为枚举对象这与手册页示例$ lsdir的行为一致path是VectorStringView类型的位置参数因此可以一次传入多个路径程序会按传入顺序逐个遍历每处理一个目录就打印一行Traversing path分隔头。3. 手册页示例及输出行为手册页给出的官方示例listdir.md#L31-L40# List directory entries of working directory $ lsdir # List directory entries of /proc directory $ lsdir /proc # List directory entries of /proc directory with POSIX names for file types $ lsdir -P /proc # List directory entries of /proc directory and print in the end the count of traversed entries $ lsdir -t /proc结合源码可以精确描述其输出格式listdir.cpp#L42-L62每个路径先输出一行Traversing path随后每个目录项输出一行固定格式name (Type: type, Inode number: ino)例如普通文件在默认模式下显示为Type: File加-P后显示为Type: DT_REG若启用-t遍历结束后追加一行Directory path has count which has being listed during the program runtime其中count是实际成功枚举到的条目数若某个路径打开失败程序向 stderr 输出Failed to open path - error并以该错误码退出不会继续处理剩余路径。一个典型运行效果对某目录执行lsdir -P时的示意格式Traversing /proc 1 (Type: DT_DIR, Inode number: 2) self (Type: DT_LNK, Inode number: 3) cpuinfo (Type: DT_REG, Inode number: 4)具体条目内容取决于运行时/proc的实际状态此处仅展示输出格式。4. 源码剖析从 pledge 到 DirIterator(NoStat)lsdir的实现只有约 70 行listdir.cpp完整调用链为main → pledge → ArgsParser 解析 → DirIterator(NoStat) 循环 → DirectoryEntry 类型名映射 → 计数输出4.1 沙箱限制pledge函数体第一步listdir.cpp#L20TRY(Core::System::pledge(stdio rpath));在 Serenity 的 pledge 机制下lsdir声明自己只使用stdio和rpath按路径解析访问文件系统两类能力之后任何超出该承诺的调用都会被拒绝。这也解释了为什么它“只受限于get_dir_entries一类只读目录遍历操作”——它既没有网络权限也没有创建文件的能力。4.2 核心循环NoStat 模式的 DirIteratorCore::DirIterator di(path, Core::DirIterator::NoStat); ... while (di.has_next()) { auto dir_entry di.next(); if (dir_entry.has_value()) { outln( {} (Type: {}, Inode number: {}), dir_entry.value().name, name_from_directory_entry_type(dir_entry.value().type), dir_entry.value().inode_number); count; } }DirIterator的NoStat标志定义于 DirIterator.h#L19-L24enum Flags { NoFlags 0x0, SkipDots 0x1, SkipParentAndBaseDir 0x2, NoStat 0x4, };NoStat的关键意义在于类型信息的来源。对照 DirectoryEntry.cpp 中的两条构造路径from_stat()第 114-123 行对每条目录项执行fstat(dirfd(d), statbuf)再从st_mode用directory_entry_type_from_stat()推断类型——每个条目都要额外一次系统调用from_dirent()第 126-134 行直接取dirent结构内嵌的de.d_type字段并映射不产生额外fstat。从源码结构看lsdir传入NoStat后走的是“直接消费 dirent 原始字段”的路径这与手册页“restrict its functionality to the get_dir_entries syscall only”的表述完全对应类型来自 dirent 自带的d_typeinode 号来自d_ino一次get_dir_entries返回的批数据即可同时覆盖两者。而这条 dirent 数据的最终来源在 LibC 的目录读取实现中可以确认——dirent.cpp#L115ssize_t nread syscall(SC_get_dir_entries, dirp-fd, dirp-buffer, size_to_allocate);即readdir类操作在 Serenity 下并非逐条读目录项而是向内核发起SC_get_dir_entries由内核按批次把多条struct dirent填入用户态缓冲区lsdir消费的就是这批原始数据。4.3 文件类型的双格式映射-P选项切换的是类型名的渲染函数listdir.cpp#L45-L49FunctionStringView(Core::DirectoryEntry::Type) name_from_directory_entry_type; if (flag_show_unix_posix_file_type) name_from_directory_entry_type Core::DirectoryEntry::posix_name_from_directory_entry_type; else name_from_directory_entry_type Core::DirectoryEntry::representative_name_from_directory_entry_type;两个映射函数定义于 DirectoryEntry.cpp#L12-L60覆盖Core::DirectoryEntry::Type枚举DirectoryEntry.h#L15-L30的全部 9 个取值Type 枚举值POSIX 名-P人类可读名默认BlockDeviceDT_BLKBlockDeviceCharacterDeviceDT_CHRCharacterDeviceDirectoryDT_DIRDirectoryFileDT_REGFileNamedPipeDT_FIFONamedPipeSocketDT_SOCKSocketSymbolicLinkDT_LNKSymbolicLinkUnknownDT_UNKNOWNUnknownWhiteoutDT_WHTWhiteout其中WhiteoutDT_WHT是 overlayfs 等联合文件系统使用的白洞类型标记出现在 Serenity 的类型枚举中说明get_dir_entries传递的类型位宽考虑了 POSIX 之外的扩展取值。4.4 -t 计数逻辑-t的统计口径值得注意计数变量count只在di.next()返回有效值时自增listdir.cpp#L51-L59即统计的是程序运行期间实际成功枚举到的条目数而不是“目录里应有的条目数”。这也与源码输出文案“which has being listed during the program runtime”的措辞一致。5. 适用前提与相关工具适用前提lsdir是 Serenity 用户态工具源码位于 Userland/Utilities/listdir.cpp构建时随用户态 Utilities 一起编译其-P选项依赖 dirent 自带d_type的有效性——若某文件系统实现不在 dirent 中填充d_type条目会显示为Unknown/DT_UNKNOWNfrom_dirent对未知dt值会落入VERIFY_NOT_REACHED实际行为取决于具体文件系统填充情况。与ls的分工ls面向人类展示目录内容与文件元信息lsdir面向需要观察“内核返回的原始 dirent”的场景例如验证某个文件系统是否正确填充d_type与d_ino。相关手册页ls(1)。6. 小结lsdir是 Serenity OS 中一个典型的“小而正”的底层工具它用pledge收窄自身权限用DirIterator::NoStat避开逐条fstat让每一个目录条目的名称、类型、inode 号都直接来自SC_get_dir_entries返回的原始 dirent 数据。理解它的实现实际上就是理解 Serenity 用户态目录遍历链路DirIterator → LibC readdir → get_dir_entries syscall → 内核目录项批处理的一次完整走查。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻