[操作系统] 打印进程树 pstree
打印进程树
简介
这是 jyy 老师的操作系统课程的 M1 实验,为了弥补一些欠缺的操作系统相关的知识。在这里实现的的 pstree
并不是严格的按照实验要求而设计的(一个原因是按要求实现的代码不可以公开),这里会看到一些不一样的简单实现,比如直接运行,没有命令行可选参数,输出格式会有所不同,编程语言所使用的是 c++
而不是 c
。
实验要求
实现一个简单的 pstree
,能够展示进程之间的树状关系。
实验环境
Arch Linux x86_64
实验设计
在我的环境中,因为使用的是 arch ,可以知道第一个进程的 pid=1
,也就是 systemd
。
在 linux
中,一切皆文件,那么我们就需要找到一个对应的目录,找到进程文件,也就是 /proc
下,每个以数字为名的子目录就代表着当前系统下运行的进程,每个进程目录下包含着多个与该进程相关的进程文件。因此,这里使用 std::filesystem::directory_iterator
对目录进行遍历。
对于进程树,我们需要知道如何获取到该进程的父进程 pid
。这里可以通过 /proc/[pid]/stat
或者可读性更好的 /proc/[pid]/status
提供该进程的信息。我们需要其中的进程名,以及其父进程 pid
,对应 stat
中的第二个和第四个字符串。
只要我们将这些信息找出来,就不难发现,这些构成了一颗树,我们打印的时候只需递归打印即可。
总的代码量很少,很简单就可以在一个 main
中实现一个简单的 pstree
了。
#include <cassert> #include <chrono> #include <cstdint> #include <fstream> #include <ranges> #include <format> #include <iostream> #include <filesystem> #include <string> #include <unordered_map> #include <vector> auto main(int argc, char* argv[]) -> int { for (int i = 0; i < argc; i ++) { assert(argv[i] != nullptr); // std::cout << std::format("argv[{}] = {}\n", i, argv[i]); } assert(argv[argc] == nullptr); std::filesystem::path proc_dir_path = "/proc"; assert(std::filesystem::exists(proc_dir_path)); std::unordered_map<std::uint64_t, std::vector<std::uint64_t>> pid_adj{}; std::unordered_map<std::uint64_t, std::string> pid_name{}; auto string_to_pid = [](const std::string &file_name, std::uint64_t &pid_result) -> bool { pid_result = 0; for (auto ch : file_name) { if (ch >= '0' && ch <= '9') { pid_result = pid_result * 10 + (ch - '0'); } else { return false; } } return true; }; for (const auto &entry : std::filesystem::directory_iterator(proc_dir_path) | std::views::filter([](const auto &entry) { return entry.status().type() == std::filesystem::file_type::directory && std::filesystem::exists(entry.path() / "stat"); }) ) { auto file_name = entry.path().filename().string(); std::uint64_t pid; if (!string_to_pid(file_name, pid)) { continue; } auto stat_stream = std::ifstream(entry.path() / "stat"); std::string _, parent_pid_str, pid_name_str; stat_stream >> _ >> pid_name_str >> _ >> parent_pid_str; std::uint64_t parent_pid; if (!string_to_pid(parent_pid_str, parent_pid)) { continue; } pid_name[pid] = pid_name_str; pid_adj[parent_pid].emplace_back(pid); } auto dump_print = [&](auto &self, std::uint64_t current_pid, std::string indent = "", bool is_last = true) -> void { const auto marker = is_last ? "└───" : "├───"; std::cout << std::format("{}{}{}({})\n", indent, marker, pid_name[current_pid], current_pid); if (!pid_adj.contains(current_pid)) { return; } indent += is_last ? " " : "│ "; const auto &next = pid_adj[current_pid]; for (std::size_t i = 0; i < next.size(); i ++) { self(self, next[i], indent, i + 1 == next.size()); } }; constexpr std::uint64_t init_pid = 1; dump_print(dump_print, init_pid); }
本文作者:フランドール·スカーレット
本文链接:https://www.cnblogs.com/FlandreScarlet/p/17975765
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步