[操作系统] 打印进程树 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);
}
posted @ 2024-01-19 22:23  フランドール·スカーレット  阅读(345)  评论(0)    收藏  举报