打印进程树
简介
这是 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);
}
标签:std,操作系统,auto,打印,pid,进程,include,pstree,name
From: https://www.cnblogs.com/FlandreScarlet/p/17975765