
Serenity OS 进程检索实战pgrep 手册深度解读与源码实现剖析【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenitypgrep是 Serenity Operating System 中用于按进程名或正则表达式查找进程 PID的核心命令行工具其手册页位于 Base/usr/share/man/man1/pgrep.md实现源码位于 Userland/Utilities/pgrep.cpp。本文将完整继承该手册的全部选项与用法并结合源码逐行剖析其匹配机制、UID 过滤、进程年龄筛选、输出排序与退出码语义帮助读者既能立刻上手使用也能深入理解 Serenity OS 进程查询工具链pgrep/pidof/ps的底层协作方式。一、命令概览pgrep 是什么pgrep通过进程名来查找进程输出的是匹配进程的 PID进程标识符。它面向脚本与日常终端操作设计你可以把它当作根据名字找进程号的专用工具也可以将其输出通过管道交给kill、wait等命令做进程管理。手册中给出的完整用法如下$ pgrep [--count] [-d delimiter] [--ignore-case] [--list-name] [--newest] [--oldest] [--older seconds] [--uid uid-list] [--invert-match] [--exact] process-name其中process-name是必填的位置参数即要搜索的进程名其余全部为可选选项用于裁剪匹配范围与调整输出格式。二、选项全解每个参数的语义与源码对应手册列出了 11 个选项下表给出每个选项的短/长格式、作用并标注其在 pgrep.cpp 中的参数解析位置短选项长选项作用源码对应-c--count抑制正常输出仅打印匹配进程的个数pgrep.cpp-d--delimiter设置多个 PID 之间的分隔字符串pgrep.cpp-i--ignore-case匹配时忽略大小写pgrep.cpp-l--list-name除 PID 外同时列出进程名pgrep.cpp-n--newest仅选择最近创建的进程pgrep.cpp-o--oldest仅选择最早创建的进程pgrep.cpp-O--older仅选择创建时间早于指定秒数的进程pgrep.cpp-U--uid仅选择 UID 属于给定逗号分隔列表的进程可传登录名或数字用户 IDpgrep.cpp-x--exact仅选择进程名与给定模式完全匹配的进程pgrep.cpp-v--invert-match选择不匹配的行反向匹配pgrep.cpp2.1-c, --count只数个数抑制正常输出改为打印匹配进程的数量。源码在匹配完成后直接输出matches.size()if (display_number_of_matches) { outln({}, matches.size()); }适用于脚本判断是否存在满足条件的进程例如配合退出码使用见下文第七节。2.2-d, --delimiter自定义多 PID 分隔符当一次匹配到多个进程时PID 之间默认以换行符分隔源码中pid_delimiter初始化为\nsv。通过-d可以改为任意字符串例如用空格或冒号拼接便于直接嵌入脚本$ pgrep -d bash源码输出循环中除第一个 PID 外每个 PID 前都会先打印该分隔符if (displayed_at_least_one) out({}sv, pid_delimiter); out({}sv, match.pid);2.3-i, --ignore-case忽略大小写匹配默认匹配区分大小写。开启后匹配过程变为大小写不敏感。其实现是向正则表达式引擎传递PosixFlags::Insensitive标志PosixOptions options {}; if (case_insensitive) options | PosixFlags::Insensitive;例如进程名为Bash时pgrep -i bash也能命中。2.4-l, --list-name同时输出进程名默认只输出 PID加上-l后每个 PID 后追加一个空格和进程名if (list_process_name) out( {}sv, match.name);便于快速确认这个 PID 对应的确实是我想找的进程。2.5-n, --newest与-o, --oldest按创建时间取极值-n, --newest只输出最近创建最新的一个匹配进程-o, --oldest只输出最早创建的一个匹配进程。两个选项互斥同时使用会被拒绝源码在解析完成后立即检查if (newest_only oldest_only) { warnln(The -n and -o options are mutually exclusive); args_parser.print_usage(stderr, args.strings[0]); return 1; }实现上所有匹配先按creation_time升序排序然后取首尾quick_sort(matches, [](auto const a, auto const b) { return a.creation_time b.creation_time; }); if (newest_only) matches { matches.last() }; else if (oldest_only) matches { matches.first() };注意-n/-o只影响输出阶段--count仍统计全部匹配数量-l、-d对单 PID 输出不产生额外影响。2.6-O, --older seconds筛选存活时长只选择已存活超过指定秒数的进程。其实现逻辑不是比较当前时间减去秒数而是先计算出阈值时间点再剔除创建时间晚于该阈值的进程auto now_time UnixDateTime::now(); display_if_older_than now_time - Duration::from_seconds(static_casti64(number.value())); // ... if (display_if_older_than.has_value() it.creation_time display_if_older_than.value()) continue; // 创建时间晚于阈值 → 不满足较老跳过seconds必须是合法的无符号 64 位整数且不超过i64最大值否则该值不被接受、选项视为无效。2.7-U, --uid uid-list按用户过滤只选择 UID 位于给定逗号分隔列表中的进程。列表中的每一项既可以是数字 UID也可以是登录名——登录名会通过Core::Account::from_name()解析成 UIDfor (auto user_string : comma_separated_users.split_view(,)) { auto maybe_uid user_string.to_numberuid_t(); if (maybe_uid.has_value()) { uids_to_filter_by.set(maybe_uid.value()); } else { auto maybe_account Core::Account::from_name(user_string, Core::Account::Read::PasswdOnly); if (maybe_account.is_error()) { warnln(Could not find user {}: {}, user_string, maybe_account.error()); return false; } uids_to_filter_by.set(maybe_account.release_value().uid()); } }因此以下两条命令等价$ pgrep -U 1000 bash $ pgrep -U anon bash # 假设 anon 的 UID 为 1000若传入的用户名不存在命令会报错Could not find user ...并以失败结束。2.8-x, --exact精确匹配进程名默认使用正则表达式匹配加上-x后要求进程名与模式完全一致。源码将该模式包装为锚定正则if (exact_match) { exact_pattern_builder.appendff(^({})$, pattern); pattern exact_pattern_builder.string_view(); }即把用户输入包进^(...)$从而禁止部分匹配。例如进程名myapp-server不会被pgrep -x myapp命中但会被pgrep myapp命中。2.9-v, --invert-match反向匹配选择不匹配指定模式的进程手册原文为 Select non-matching lines。匹配核心是这一行auto result re.match(it.name, PosixFlags::Global); if (result.success ^ invert_match) { // ... 进入过滤与收集 }result.success ^ invert_match的含义是默认情形下只要正则命中就收集开启-v后只有未命中的进程才被收集。其余过滤条件UID、进程年龄在反向匹配的基础上继续生效。2.10 位置参数process-name必填的进程名模式。结合-x使用时是精确字符串不带-x时是 POSIX 扩展正则表达式详见下节。三、模式匹配机制POSIX 扩展正则从源码可见进程名匹配由LibRegex库中的RegexPosixExtended完成RegexPosixExtended re(pattern, options); if (re.parser_result.error ! regex::Error::NoError) { return 1; }这意味着process-name实质上是一个POSIX 扩展正则表达式ERE而不仅仅是字面量。例如# 匹配所有以 web 开头、后跟任意内容的进程 $ pgrep web.*同时--ignore-case通过PosixFlags::Insensitive注入匹配选项见 2.3 节。若正则表达式本身非法命令返回退出码 1 且不输出任何 PID。四、数据来源与安全边界/sys/kernel/processespgrep获取进程列表的方式值得特别关注它不遍历/proc而是读取内核导出的/sys/kernel/processes伪文件并通过Core::ProcessStatisticsReader解析成结构化数据TRY(Core::System::pledge(stdio rpath)); TRY(Core::System::unveil(/sys/kernel/processes, r)); TRY(Core::System::unveil(/etc/group, r)); TRY(Core::System::unveil(/etc/passwd, r)); TRY(Core::System::unveil(nullptr, nullptr));pledge(stdio rpath)只允许标准 I/O 与相对路径读取收紧进程权限面unveil将可见文件系统限定为三个路径进程统计文件、用户组文件、密码文件-U用登录名时需要解析 UID。ProcessStatisticsReader::get_all()会打开该文件并解析 JSONProcessStatisticsReader.cppauto proc_all_file TRY(Core::File::open(/sys/kernel/processessv, Core::File::OpenMode::Read)); return get_all(*proc_all_file, include_usernames);每个进程的creation_time取自 JSON 中的creation_time字段纳秒精度最终转换为UnixDateTimeprocess.creation_time UnixDateTime::from_nanoseconds_since_epoch(process_object.get_i64(creation_timesv).value_or(0));字段结构定义在 ProcessStatisticsReader.h其中pid、uid、name、creation_time正是pgrep各选项所依赖的核心字段。同目录的ps、pidof工具也复用同一套读取逻辑构成统一的进程信息基础设施。五、输出顺序与组合过滤匹配结果在输出前会统一按创建时间升序排序由AK::QuickSort完成quick_sort(matches, [](auto const a, auto const b) { return a.creation_time b.creation_time; });因此默认输出顺序为旧进程在前、新进程在后。所有过滤选项-U、-O都在收集阶段作用于已命中的进程排序只影响最终展示顺序。组合使用示例# 列出用户 anon 名下所有名字含 bash 的进程及其 PID 与进程名 $ pgrep -l -U anon bash # 查找创建时间超过 60 秒、名字以 web 开头的进程数量 $ pgrep -c -O 60 web.* # 只显示名字完全等于 sshd 的进程中最新一个的 PID $ pgrep -n -x sshd六、与 pidof、ps 的分工协作手册See also一节将pgrep与pidof、ps并列三者构成 Serenity OS 进程查询的完整工具链pgrep按正则/精确名字查 PID支持复杂过滤UID、年龄、反向、极值、计数面向脚本化pidof按字面量进程名查 PID实现中直接比较it.name ! options.process_name见 pidof.cpp支持-o排除指定 PID含%PPID特例、-s单发模式、-S自定义分隔符语义更简单直接ps列出进程并支持自定义列格式-o uid,pid,ppid,...、按 TTY/PID/PPID/用户筛选侧重信息展示而非查找。三者都依赖Core::ProcessStatisticsReader读取/sys/kernel/processes因此对进程视图的理解可以相互印证先用pgrep/pidof定位 PID再用ps -p pid查看详情是推荐的排查链路。七、退出码语义脚本判定的关键源码最后一行定义了退出码规则return matches.is_empty() ? 1 : 0;找到至少一个匹配进程 → 退出码0没有任何匹配或匹配数为 0→ 退出码1。这与grep的找到返回 0、没找到返回 1约定一致使pgrep可以直接用于脚本条件判断if pgrep -x sshd /dev/null; then echo sshd is running else echo sshd is not running fi注意-c模式下同样遵循该规则即使-c输出0退出码仍为 1脚本应优先用退出码而非解析输出来做判断。八、快速参考典型用法一览需求命令查找所有名为 bash 的进程 PIDpgrep bash只输出匹配数量pgrep -c bash忽略大小写匹配pgrep -i BASH输出 PID 与进程名pgrep -l bash名字必须完全等于 bashpgrep -x bash输出最新/最早的一个pgrep -n bash/pgrep -o bash只找存活超过 120 秒的pgrep -O 120 bash只找指定用户的pgrep -U anon bash反向匹配不含 bash 的进程pgrep -v bash多个 PID 用逗号分隔pgrep -d , bash配合正则查找 web 系进程pgrep web.*九、小结pgrep表面上是按名查 PID的小工具背后却是 Serenity OS 进程基础设施的缩影通过pledge/unveil收紧权限经由/sys/kernel/processes获取内核进程快照由ProcessStatisticsReader统一解析再用LibRegex的 POSIX 扩展正则完成匹配最后以grep式退出码服务脚本生态。无论是日常终端操作、脚本自动化还是深入阅读 pgrep.cpp 学习参数解析与进程过滤的最佳实践这份手册与源码都是很好的起点。【免费下载链接】serenityThe Serenity Operating System 项目地址: https://gitcode.com/GitHub_Trending/se/serenity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考