SerenityOSw命令完全指南:查看当前登录用户与终端活动状态
【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity
导读
w是 SerenityOS 系统中用于查看当前已登录用户及其终端活动状态的系统工具,其作用与类 Unix 系统中的同名命令一致。本文以 Base/usr/share/man/man1/w.md 手册页为骨架,结合其实现源码 Userland/Utilities/w.cpp、登录会话记录工具utmpupdate以及终端应用 Userland/Applications/Terminal/main.cpp 的调用链,带你完整掌握w的用法、输出字段含义、底层数据来源(/var/run/utmp与/sys/kernel/processes),以及它与其他用户管理命令(whoami、usermod、utmpupdate)之间的关系。读完本文,你将能够熟练使用w排查多用户登录状态、定位空闲会话,并理解 SerenityOS 中登录会话记录机制的完整数据流。
命令概览
w命令显示关于当前登录用户的信息,包括每个登录会话对应的终端(TTY)、登录时间、空闲时长以及当前正在前台运行的命令。其基本形态如下:
$ w [--no-header] [user]user:可选位置参数。指定后,w只显示该指定用户的登录会话信息。-h,--no-header:可选开关。指定后不显示表头行(USER TTY LOGIN@ IDLE WHAT)。
关于命令的完整选项与参数说明,可以直接查阅仓库内手册 Base/usr/share/man/man1/w.md。
参数解析的实现
w使用 SerenityOS 自带的Core::ArgsParser完成参数解析,对应源码位于 Userland/Utilities/w.cpp:
bool hide_header = false; StringView username_to_filter_by; Core::ArgsParser args_parser; args_parser.add_option(hide_header, "Don't show the header", "no-header", 'h'); args_parser.add_positional_argument(username_to_filter_by, "Only show information about the specified user", "user", Core::ArgsParser::Required::No); args_parser.parse(args);可以看到:
-h/--no-header与hide_header布尔变量绑定,决定是否输出表头;user是可选位置参数(Required::No),与username_to_filter_by绑定,用于按用户名过滤输出。
输出格式详解
w默认输出一个表头加若干数据行,每个登录会话一行。表头与数据行的格式由源码中的两处格式化字符串决定(Userland/Utilities/w.cpp 与 Userland/Utilities/w.cpp):
USER TTY LOGIN@ IDLE WHAT| 列名 | 含义 | 生成方式(源码依据) |
|---|---|---|
USER | 登录用户名 | 由会话记录中的uid通过/etc/passwd反查得到;若反查失败则直接显示数字 UID |
TTY | 伪终端名 | 根据 TTY 设备的主设备号映射为pts:N或tty:N |
LOGIN@ | 登录时刻 | 由会话记录中的login_at时间戳按%b%d %H:%M:%S格式化(如Sep09 04:11:23) |
IDLE | 空闲时长 | 当前时间减去 TTY 设备文件的st_mtime(最后一次活动时间),以秒为单位显示 |
WHAT | 当前前台命令 | 在该 TTY 上运行的、PID 等于其进程组 ID 的进程名;无法获取时显示n/a |
一个典型的输出形如:
USER TTY LOGIN@ IDLE WHAT anon pts:0 Sep09 04:05:33 12s Shell root tty:1 Sep09 03:58:01 600s n/aTTY 名称的映射逻辑
源码中的tty_stat_to_pseudo_name函数(Userland/Utilities/w.cpp)根据 TTY 设备的主设备号把设备路径转换为人类可读的名称:
- 主设备号
201→pts:{minor}(伪终端,即终端模拟器使用的会话); - 主设备号
4→tty:{minor}(物理/系统控制台终端); - 其他类型返回
Unknown TTY device type错误。
空闲时长(IDLE)的计算原理
w通过stat获取 TTY 设备文件的st_mtime(最后修改时间)作为"最后一次终端活动时间",再用当前 Unix 时间戳减去它得到空闲秒数(Userland/Utilities/w.cpp):
auto maybe_stat = Core::System::stat(tty); if (!maybe_stat.is_error()) { auto stat = maybe_stat.release_value(); auto idle_time = now - stat.st_mtime; if (idle_time >= 0) { builder.appendff("{}s", idle_time); idle_string = TRY(builder.to_string()); } }如果无法对 TTY 执行stat(例如会话记录中的 TTY 已不存在),则IDLE与WHAT均显示为n/a。
当前前台命令(WHAT)的推断
WHAT列通过进程统计信息反查:遍历 Userland/Libraries/LibCore/ProcessStatisticsReader.h 提供的ProcessStatistics列表,找到tty与当前 TTY 匹配、且pid == pgid(进程 ID 等于进程组 ID,即该进程组的组长)的进程,取其name作为前台命令(Userland/Utilities/w.cpp)。这一"进程组组长即前台进程"的推断方式与常规 Unix 会话模型一致。
按用户过滤
当传入user位置参数时,w只输出该用户的会话行。源码中通过用户名精确字符串比较实现过滤(Userland/Utilities/w.cpp):
if (!username_to_filter_by.is_empty() && username_to_filter_by != username) return {};例如:
$ w anon只显示用户anon的所有登录会话;不指定参数则显示所有用户。
底层数据来源:/var/run/utmp与/sys/kernel/processes
w的输出并非凭空生成,而是来自两个数据源:
/var/run/utmp:登录会话记录的 JSON 文件。w打开并读取该文件(Userland/Utilities/w.cpp),文件内容是一个 JSON 对象,以 TTY 名为键,每个会话包含uid、pid、from、login_at等字段。若解析失败(非 JSON 对象),w会向标准错误输出Error: Could not parse /var/run/utmp并返回退出码 1。/sys/kernel/processes:内核导出的进程统计信息。w通过Core::ProcessStatisticsReader::get_all()(Userland/Utilities/w.cpp)读取,用于推断各 TTY 上前台运行的命令(WHAT列)。
安全加固:pledge 与 unveil
w在启动时调用了 SerenityOS 的沙箱原语pledge与unveil(Userland/Utilities/w.cpp),声明自己只需要"标准 I/O + 读路径"权限,并只允许读取以下文件/目录:
/dev(TTY 设备,用于stat计算空闲时长)/etc/group、/etc/passwd(用户与组信息反查)/etc/timezone(时间相关)/var/run/utmp(登录会话记录)/sys/kernel/processes(进程统计)
这是一种典型的"最小权限"设计:即使w被攻破,也无法写入任何文件或访问未声明的资源。
登录会话从何而来:utmpupdate与终端模拟器的配合
w读取的/var/run/utmp由另一个命令维护:utmpupdate。其用法为:
$ utmpupdate [--create] [--delete] [--PID PID] [--from From] <tty>| 选项 | 说明 |
|---|---|
-c,--create | 为指定 TTY 创建登录会话记录 |
-d,--delete | 删除指定 TTY 的登录会话记录 |
-p PID,--PID PID | 记录会话对应的进程 PID |
-f From,--from From | 记录会话来源(如Terminal) |
tty(位置参数) | TTY 名称 |
utmpupdate的实现位于 Userland/Utilities/utmpupdate.cpp:--create时会以当前调用者 UID、给定 PID 与当前时间构造一条记录写入 JSON;--delete时按 TTY 名移除记录。-c与-d互斥,同时指定会报错并返回 1。
Terminal 应用的调用链
SerenityOS 自带的终端模拟器 Terminal 在创建/关闭伪终端会话时,会以子进程方式调用utmpupdate来登记或注销会话(Userland/Applications/Terminal/main.cpp):
Array utmp_update_command { "-f"sv, "Terminal"sv, "-p"sv, pid_string.bytes_as_string_view(), (create ? "-c"sv : "-d"sv), tty, }; auto utmpupdate_pid = TRY(Core::Process::spawn("/bin/utmpupdate"sv, utmp_update_command, {}, Core::Process::KeepAsChild::Yes));这正是w中LOGIN@、TTY等字段得以填充的源头。同时,utmpupdate自身也通过pledge("stdio wpath cpath")与unveil("/var/run/utmp", "rwc")将权限限制在读写/var/run/utmp(Userland/Utilities/utmpupdate.cpp)。
构建与集成位置
w属于 SerenityOS 用户态核心命令集,被列入REQUIRED_TARGETS(构建系统强制要求的目标),源码条目位于 Userland/Utilities/CMakeLists.txt,并在 Userland/Utilities/CMakeLists.txt 的必需命令列表中。这意味着在任何完整构建的 SerenityOS 镜像中,/bin/w都会随系统一起编译安装。
常见用法示例
# 查看所有登录用户及其终端活动 $ w # 不显示表头,便于脚本解析 $ w --no-header # 只查看指定用户(如 anon)的会话 $ w anon # 组合使用:只看 anon 且不要表头 $ w -h anon-h与位置参数可以任意组合,顺序不影响解析结果,因为参数均由Core::ArgsParser统一处理。
相关命令
w通常与以下命令配合使用,形成一个完整的"用户与会话管理"工具链:
whoami(1):打印当前有效 UID 对应的用户名。示例:whoami输出anon,# whoami(root)输出root。它回答的是"我是谁",而w回答的是"现在都有谁登录着、在干什么"。utmpupdate(1):维护/var/run/utmp登录会话记录,是w数据的上游写入者,其手册页也反向链接回w。usermod(8):管理系统用户账户(如修改用户所属组、Shell 等),与查看会话状态的w互补。
小结
w虽然是一个"小"命令,但它的实现串起了 SerenityOS 中多条关键机制:JSON 格式的系统状态文件(/var/run/utmp)、内核进程统计接口(/sys/kernel/processes)、pledge/unveil最小权限模型,以及终端模拟器与会话登记工具之间的进程协作。理解w的完整数据流,也就理解了 SerenityOS 多用户会话管理的核心脉络。
【免费下载链接】serenityThe Serenity Operating System 🐞项目地址: https://gitcode.com/GitHub_Trending/se/serenity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考