Process Manager for Linux Phase 1
2021/4/7 7:06:35
本文主要是介绍Process Manager for Linux Phase 1,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Process Manager for Linux Phase 1
我的课设!
Requirement
这部分的设计要求是:
实现一个基于控制台的进程管理器,包含以下功能:
- 记录:将获取到的进程信息每一分钟保存到某个文档中,文档名称为当前的时间戳。
- 查找:用户输入要查找的进程ID,系统调用查找函数进行查找并显示结果。
- 显示:当用户输入指令时,或者每一分钟,显示当前的最新进程信息,包括进程的ID、进程的名
称、进程的位数以及进程的CPU占有率。 - 排序:可按照进程ID大小、进程名字、CPU占有率进行排序。
Code Design
整体上,我们给整个 Project 设置了这样的结构:
我们使用 Runtime
中的 class Loop
实现程序主题运行时刻的 Read-Evaluate-Print Loop 。
在 Item
中的一些类主要维护程序中涉及的各类数据。class Process
维护单个进程;class ProcessList
维护进程列表,同时实现更新、排序、查找等功能的接口;class Tokens
维护用户输入的指令信息。
此外,man_text
中用于集中存放一些手册、输出信息等内容,便于管理维护。iostaff
实现一些终端彩色输出,字符串标准化函数等输入输出杂项。
接下来我们分别看各部分的实现。
Utils
iostaff
// iostaff.h #pragma once #include <string> #include <ostream> class Form { public: int type; Form(int x): type(x) {} friend std::ostream& operator << (std::ostream &os, const Form &itm); }; const Form endo(0), tab(-1), bold(1), red(31), blue(34), green(32), highlit(43), italic(3); int StringToInt(const std::string &s); std::string NameStandlize(const std::string &s); void PrintTableline(); // iostaff.cpp #include <cctype> #include <iostream> #include "iostaff.h" std::ostream& operator << (std::ostream& os, const Form& itm) { switch (itm.type) { case 0: os << "\033[39m\033[0m"; break; case -1: os << "\t\t"; break; default: os << "\033[" << itm.type << 'm'; } return os; } int StringToInt(const std::string &s) { int ret = 0; for (auto digit: s) { if (!isdigit(digit)) { return 0; } ret = ret * 10 + digit - 48; } return ret; } std::string NameStandlize(const std::string &s) { std::string ret; for (auto cht: s) { if (isalpha(cht)) { ret.push_back(toupper(cht)); } } return ret; } void PrintTableline() { std::cout << endo << blue; for (int i = 0; i != 80; ++i) { std::cout << "-"; } std::cout << endo << std::endl; }
基于这里 Form
类的实现,我们可以通过类似于:
int a, b; std::cin >> a >> b; std::cout << bold << italic << red << a << " " << b << endo << std::endl;
的方式,方便控制输出粗体、斜体、字体颜色内容,其中 endo
可清空当前流中的格式。
Text
man_text
// man_text.h void TableHeading(); void def_manual(); void WaitInput(); // man_text.cpp #include <iostream> #include "../Utils/iostaff.h" void def_manual() { using namespace std; PrintTableline(); cout << bold << blue << "Command" << tab << "Args" << tab << "Function" << endo << endl; PrintTableline(); cout << bold << green << "help" << endo << tab << "None" << tab << "Print the Command Manual" << endl; cout << bold << green << "quit" << endo << tab << "None" << tab << "Quit the Task Manager for Linux" << endl; cout << bold << green << "update" << endo << tab << "None" << tab << "Update the Process Infomation right now" << endl; cout << bold << green << "find" << endo << tab << green << "[Pid]" << endo << tab << "Find a process by its pid, or other dimensions" << endl; cout << bold << green << "sort" << endo << tab << green << "[COL] [ASC/DEC]" << endo << " Sort info by given coloum name in given order" << endl; cout << bold << green << "display" << endo << tab << green << "[NUM]" << endo << tab << "Motify the number of processes to display" << endl; PrintTableline(); } void TableHeading() { using namespace std; cout << bold << green << "PID" << tab << "%CPU" << tab << "%MEM" << tab << "COMMAND" << tab << endo << endl; } void WaitInput() { std::cout << "TaskM>>" << bold << green << "> " << endo; }
这里只是基于 iostaff
输出了一些简单的文本信息。
Item
Tokens
// tokens.h #pragma once #include <istream> #include <vector> #include <string> class Tokens { private: std::vector<std::string> CmdLine; void AddElements(std::string s); public: void Print(); bool empty() const; std::string GetHead() const; std::string GetArgs(int ith) const; friend std::istream& operator >> (std::istream& is, Tokens& itm); }; // tokens.cpp #include <string> #include <iomanip> #include <cstdlib> #include <iostream> #include "tokens.h" #include "../Utils/iostaff.h" void Tokens::AddElements(std::string s) { CmdLine.push_back(s); } bool Tokens::empty() const { return CmdLine.empty(); } std::string Tokens::GetHead() const { return NameStandlize(CmdLine[0]); } std::string Tokens::GetArgs(int ith) const { if (ith >= CmdLine.size()) { return std::string(); } return CmdLine[ith]; } std::istream& operator >> (std::istream& is, Tokens& ret) { std::string buf; std::getline(std::cin, buf); buf = buf + ' '; std::string::size_type ind = 0; while (true) { while (buf[ind] == ' ') ++ind; auto pos = buf.find(' ', ind); if (pos == std::string::npos) { break; } ret.AddElements(buf.substr(ind, pos - ind)); ind = pos + 1; } return is; } void Tokens::Print() { for (auto itm: CmdLine) { std::cout << "[" << itm << "] "; } std::cout << std::endl; }
只是一些简单的字符串处理操作。
Process
// process.h #pragma once #include <string> #include <vector> #include <iostream> class Process { private: int pid; int VirtualMem, ResidentSiz; double Ratio_CPU, Ratio_MEM; std::string RuningBits; std::string UserName, CommandStr, tty, RunStat, start, SysTime; public: friend std::istream& operator >> (std::istream &ins, Process &itm); friend std::ostream& operator << (std::ostream &ous, const Process &itm); int GetPid() const; double GetRatioCPU() const; double GetRatioMEM() const; std::string GetCmd() const; // std::string& GetBit(); }; // process.cpp #include <iomanip> #include <istream> #include <string> #include "process.h" #include "../Utils/iostaff.h" int Process::GetPid() const { return pid; } double Process::GetRatioCPU() const { return Ratio_CPU; } double Process::GetRatioMEM() const { return Ratio_MEM; } std::string Process::GetCmd() const { return CommandStr; } // std::string& Process::GetBit() { // return RuningBits; // } std::istream& operator >> (std::istream &ins, Process &itm) { ins >> itm.UserName >> itm.pid >> itm.Ratio_CPU >> itm.Ratio_MEM; ins >> itm.VirtualMem >> itm.ResidentSiz >> itm.tty >> itm.RunStat; ins >> itm.start >> itm.SysTime; std::ostringstream oss; getline(ins, itm.CommandStr); return ins; } std::ostream& operator << (std::ostream &ous, const Process &itm){ ous << itm.pid << tab << itm.Ratio_CPU << tab << itm.Ratio_MEM << tab << itm.CommandStr; return ous; }
这里原定是要通过 file -L /proc/PID/exe | grep -o '..-bit'
获取进程的运行位数的。但该批量执行时,可能出现需要 root
权限、进程 ID 无效、调用时间超限等问题。且考虑到 Linux 下多运行位数进程的特性并不显著,因此放弃了该功能的实现。
process_list
// process_list.h #pragma once #include <vector> #include <functional> #include "process.h" typedef std::vector<Process>::iterator pit; class ProcessList { private: std::vector<Process> lst; std::string DefaultCol; bool Defaultinc; public: ProcessList(); void UpdateList(); void PrintList(bool FoundFlag, int Siz = 10); bool FindProcess(int TargetPid); void SortCol(std::string col, bool increasing); }; // process_list.cpp #include <ctime> #include <string> #include <iomanip> #include <fstream> #include <iostream> #include <algorithm> #include <functional> #include "process.h" #include "process_list.h" #include "../Utils/iostaff.h" #include "../Text/man_text.h" ProcessList::ProcessList() { DefaultCol = "CPU"; Defaultinc = false; } void ProcessList::UpdateList() { using namespace std; ostringstream oss; auto CurTime = time(nullptr); oss << put_time(gmtime(&CurTime), "%F-%T"); string LogFile = "Logs/" + oss.str() + ".log", Heading; system(("ps auxc > " + LogFile).c_str()); ifstream FileIn(LogFile); lst.clear(); Process itm; getline(FileIn, Heading); while (FileIn >> itm) { lst.push_back(itm); oss.clear(); } SortCol(DefaultCol, Defaultinc); } bool ProcessList::FindProcess(int TargetPid) { for (pit ite = lst.begin(); ite != lst.end(); ++ite) { if (ite->GetPid() == TargetPid) { std::rotate(lst.begin(), ite, ite + 1); return false; } } std::cerr << red << "Cannot Find the given process" << endo << std::endl; return true; } void ProcessList::PrintList(bool FoundFlag, int Siz) { using namespace std; system("clear"); PrintTableline(); TableHeading(); if (FoundFlag) cout << bold << red; cout << lst[0] << endo << endl; for (int i = 1; i != Siz; ++i) { cout << lst[i] << endl; } PrintTableline(); } // Done with the help from Barry on stackoverflow.com void ProcessList::SortCol(std::string col, bool increasing) { DefaultCol = col; Defaultinc = increasing; auto sort_by = [&](auto projection) { if (increasing) { std::ranges::sort(lst, std::less(), projection); } else { std::ranges::sort(lst, std::greater(), projection); } }; if (col == "PID") sort_by(&Process::GetPid); else if (col == "CPU") sort_by(&Process::GetRatioCPU); else if (col == "MEM") sort_by(&Process::GetRatioMEM); else if (col == "COMMAND") sort_by(&Process::GetCmd); }
这里的 UpdateList
函数是基于 /bin/ps
获取进程信息。用 C++11 中的 std::put_time
结合字符串流操作,比较便利地处理了时间信息。
FindProcess
中使用了 std::rotate
将所查找的进程滚动至列表表头,便于后续显示输出操作。
SortCol
的实现使用了 C++20 新标准中的 ranges
库。最初实现时遇到了一些类型系统上的问题。在 Stack Overflow 上提问后,在 @Barry 的帮助下实现了该函数。具体参见 https://stackoverflow.com/questions/66901310
Runtime
loop
// loop.h #pragma once #include <mutex> #include <ctime> #include <thread> #include "../Item/tokens.h" #include "../Item/process_list.h" class Loop { private: time_t Systime; ProcessList prl; bool ElementFound; int DisplaySiz; static const int WaitingThread = 30; static const int WaitingUpdate = 60; public: Loop(); static int GetWaitUpdate(); static int GetWaitThread(); int& GetDisplaySiz(); bool& GetElementFound(); void EvalCmd(Tokens& cmd); void LogsCheck(); bool UpdateTime(); void REPLoop(); // Read-Evaluate-Print Loop }; // loop.cpp #include <ctime> #include <memory> #include <string> #include <future> #include <thread> #include <iostream> #include "loop.h" #include "../Item/tokens.h" #include "../Item/process.h" #include "../Text/man_text.h" #include "../Utils/iostaff.h" Loop::Loop() { Systime = -1; DisplaySiz = 10; } bool Loop::UpdateTime() { auto new_time = std::time(nullptr); if (Systime == -1 || new_time - Systime >= GetWaitUpdate()) { Systime = new_time; prl.UpdateList(); return true; } return false; } void Loop::LogsCheck() { system("mkdir -p Logs"); } int Loop::GetWaitUpdate() { return WaitingUpdate; } int Loop::GetWaitThread() { return WaitingThread; } bool& Loop::GetElementFound() { return ElementFound; } int& Loop::GetDisplaySiz() { return DisplaySiz; } void Loop::EvalCmd(Tokens& cmd) { GetElementFound() = false; if (cmd.empty()) { std::cerr << red << "Error Evaluating a empty Command" << std::endl; return; } if (cmd.GetHead() == "QUIT") { exit(0); } else if (cmd.GetHead() == "UPDATE") { auto new_time = std::time(nullptr); Systime = new_time; prl.UpdateList(); prl.PrintList(GetElementFound(), GetDisplaySiz()); WaitInput(); } else if (cmd.GetHead() == "FIND") { int arg = StringToInt(cmd.GetArgs(1)); if (arg == 0) { std::cerr << red << "Error Evaluating the FindArgs" << endo << std::endl; WaitInput(); } else { if (!prl.FindProcess(arg)) { GetElementFound() = true; prl.PrintList(GetElementFound(), GetDisplaySiz()); } WaitInput(); } } else if (cmd.GetHead() == "SORT") { auto col = NameStandlize(cmd.GetArgs(1)), ord = NameStandlize(cmd.GetArgs(2)); bool OrdFlag; if (ord == "ASC") OrdFlag = true; else if (ord == "DEC") OrdFlag = false; else { std::cerr << red << "Error sorting order input" << endo << std::endl; WaitInput(); } prl.SortCol(col, OrdFlag); prl.PrintList(GetElementFound(), GetDisplaySiz()); WaitInput(); } else if (cmd.GetHead() == "DISPLAY") { int arg = StringToInt(cmd.GetArgs(1)); if (arg == 0) { std::cerr << red << "Command argument invaild" << endo << std::endl; WaitInput(); } else { GetDisplaySiz() = arg; prl.PrintList(GetElementFound(), GetDisplaySiz()); WaitInput(); } } else { def_manual(); WaitInput(); } std::cout.flush(); } void Loop::REPLoop() { Tokens UserCmd; GetElementFound() = false; auto update_thread = std::thread([&] { while (true) { if (UpdateTime()) { GetElementFound() = false; prl.PrintList(GetElementFound(), GetDisplaySiz()); WaitInput(); std::cout.flush(); } std::this_thread::sleep_for( std::chrono::seconds(GetWaitThread())); } }); Tokens TempCmd; while(true) { std::cin >> TempCmd; UserCmd = std::move(TempCmd); EvalCmd(UserCmd); } update_thread.join(); }
这里为了实现自动刷新输出的同时等待用户输入的功能,我们在 144 行引入了一个自动更新线程。该线程每经过 int WaitingThread
秒苏醒一次,检查距离上次更新信息是否超过了 int WaitingUpdate
时间。其中 WaitingThread
是 WaitingUpdate
的一半。这一设置是考虑到用户可能会手动进行更新操作,可能会造成相应的影响。
不过这里的实现存在一个问题:如果用户正在输入时进行了一次自动更新,那么目前的输入信息会丢失,可能会导致一些后续问题。因此刷新不宜过分频繁。
这里的 EvalCmd
基本在调用之前实现的接口,并做了一些朴素的异常处理。
Demo
这篇关于Process Manager for Linux Phase 1的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法