0. 概述
Boost.Process提供了强大的跨平台进程管理能力,本文将介绍如何利用Boost.Process库,封装一个简化且高效的进程管理模块。该模块旨在屏蔽底层库的复杂性,提供简洁易用的API,支持Windows和Linux平台,同时兼容x86和ARM架构。
1. 设计目标
- 简化封装: 提供高级别的进程管理接口,隐藏Boost.Process底层细节。
- 跨平台支持: 确保在Windows和Linux下均稳定运行,支持多种硬件架构。
2. 实现详解
2.1 进程管理函数详解
- getTimeStamp(): 获取当前时间戳,用于计算进程运行时间。
- getCurrentProcessId(): 跨平台获取当前进程ID的实现。
- isRunning(): 检查指定进程ID的进程是否在运行。
- Windows: 使用Toolhelp32Snapshot API。
- Linux: 通过读取/proc/[pid]/status文件检查进程状态。
- killProcess(): 终止指定进程ID的进程。
- Windows和Linux: 使用不同的方法终止进程,支持终止子进程和非子进程。
- getAllProcessPidsByName(): 根据进程名获取所有匹配的进程ID列表。
- Windows: 使用Toolhelp32Snapshot获取进程列表。
- Linux: 遍历/proc目录下的进程状态文件,匹配进程名。
- startProcess(): 启动新的进程。
- Windows: 执行可执行文件并传递参数,支持创建新控制台。
- Linux: 执行命令行,并返回新进程ID。
3. 完整Cpp代码
展示了ProcessHelper命名空间中所有函数的完整实现,包括预处理指令、头文件包含和命名空间定义。
#pragma once
#include <stdint.h>
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <boost/process/extend.hpp>
#include <chrono>
#include <string>
#if defined(BOOST_WINDOWS_API)
#include <TlHelp32.h>
#include <boost/locale.hpp>
#elif defined(BOOST_POSIX_API)
#include <signal.h>
#include <sys/types.h>
#endif
namespace ProcessHelper {
static uint64_t getTimeStamp() {
std::chrono::milliseconds ms =
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
return ms.count();
}
static uint32_t getCurrentProcessId() {
#ifdef _WIN32
return GetCurrentProcessId();
#else
return ::getpid();
#endif
}
static std::string getProcessRunningTime(uint64_t startTimeStamp) // return minutes
{
auto now = getTimeStamp();
auto diff = now - startTimeStamp;
auto min = double(diff) / (1000 * 60);
return std::to_string(min);
}
#if defined(BOOST_WINDOWS_API)
static int isRunning(int pid) {
HANDLE pss = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
PROCESSENTRY32 pe = {0};
pe.dwSize = sizeof(pe);
if (Process32First(pss, &pe)) {
do {
// pe.szExeFile can also be useful
if (pe.th32ProcessID == pid) return 0;
} while (Process32Next(pss, &pe));
}
CloseHandle(pss);
return -1;
}
#elif defined(BOOST_POSIX_API)
// 0 : running, -1 : exit, -2 : zombie
static int isRunning(int pid) {
if (0 == kill(pid, 0)) {
std::string path = std::string("/proc/") + std::to_string(pid) + "/status";
std::ifstream fs;
fs.open(path);
if (!fs) return -1;
std::string line;
while (getline(fs, line)) {
std::size_t found = line.find("State:");
if (found == std::string::npos)
continue;
else {
found = line.find("zombie");
if (found == std::string::npos)
return 0;
else
return -2; // zombie
}
}
} else
return -1;
}
#endif
static std::tuple<bool, std::string> killChildProcess(int pid) {
std::string err;
bool ret = false;
try {
auto id = boost::process::pid_t(pid);
boost::process::child pros(id);
std::error_code ec;
pros.terminate(ec);
if (ec.value() == 0) return std::make_tuple(true, err);
} catch (boost::process::process_error &exc) {
err = exc.what();
}
return std::make_tuple(false, err);
}
static std::tuple<bool, std::string> killProcess(int pid, bool isChild = true) {
std::string err;
bool ret = false;
#if defined(BOOST_WINDOWS_API)
return killChildProcess(pid);
#elif defined(BOOST_POSIX_API)
if (isChild)
return killChildProcess(pid);
else // if not a child process,will not kill the process correctly
{
std::string cmd("kill -9 ");
cmd += std::to_string(pid);
auto ret = boost::process::system(cmd);
if (ret == 0) return std::make_tuple(true, err);
}
return std::make_tuple(false, err);
#endif
}
static std::tuple<std::vector<int>, std::string> getAllProcessPidsByName(const std::string &processName) {
std::vector<int> pids;
std::string err;
try {
#if defined(BOOST_WINDOWS_API)
std::wstring wName;
std::string exe(".exe");
wName = boost::locale::conv::to_utf<wchar_t>(processName + exe, "GBK");
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W entry;
entry.dwSize = sizeof entry;
if (!Process32FirstW(hSnapShot, &entry)) {
return std::make_tuple(std::move(pids), err);
}
do {
if (std::wstring(entry.szExeFile) == wName) {
pids.emplace_back(entry.th32ProcessID);
}
} while (Process32NextW(hSnapShot, &entry));
#elif defined(BOOST_POSIX_API)
boost::filesystem::path path = "/proc";
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator iter(path); iter != end; iter++) {
boost::filesystem::path p = *iter;
std::ifstream statusFile;
statusFile.open(p.string() + std::string("/status"));
if (!statusFile) continue;
std::string statusContent;
getline(statusFile, statusContent);
std::vector<std::string> a;
boost::algorithm::split(a, statusContent, boost::algorithm::is_any_of(":"), boost::algorithm::token_compress_on);
if (boost::algorithm::trim_copy(a[1]) == processName) {
pids.push_back(std::stoi(p.leaf().string()));
}
statusFile.close();
statusFile.clear();
}
#endif
} catch (boost::process::process_error &exc) {
err = exc.what();
}
return std::make_tuple(std::move(pids), err);
}
static std::tuple<int, std::string> startProcess(const std::string &processName, const std::string &processArgv) {
int pid = -1;
std::string err;
try {
#if defined(BOOST_WINDOWS_API)
auto p = processName + ".exe";
if (!boost::filesystem::exists(p)) {
p = boost::filesystem::current_path().string() + "/" + p;
if (!boost::filesystem::exists(p)) {
err = "process not exist";
return std::make_tuple(pid, err);
}
}
boost::process::child c(
p, processArgv, boost::process::extend::on_setup = [](auto &exec) {
exec.creation_flags |= boost::winapi::CREATE_NEW_CONSOLE_;
});
#elif defined(BOOST_POSIX_API)
auto p = processName;
p = boost::filesystem::current_path().string() + "/" + p;
if (!boost::filesystem::exists(p)) {
err = "process not exist";
return std::make_tuple(pid, err);
}
p = p + " " + processArgv;
boost::process::child c(p);
#endif
pid = c.id();
// detach as a single process
c.detach();
} catch (boost::process::process_error &exc) {
err = exc.what();
pid = -1;
}
return std::make_tuple(pid, err);
}
} // namespace ProcessHelper
4. 测试代码示例
展示了如何使用ProcessHelper命名空间中的函数来启动进程、检查进程状态、获取进程列表并终止进程的示例代码。该部分演示了模块的实际应用场景和功能验证。
#include "processhelper.hpp"
using namespace ProcessHelper;
int main() {
std::string processArgv = "test.txt";
std::string processName = "notepad";
// 启动进程
auto newtup = ProcessHelper::startProcess(processName, processArgv);
auto pid = std::get<0>(newtup);
// 查询进程是否在运行
if (0 != ProcessHelper::isRunning(pid)) {
std::cerr << "process " << pid << " is not running!" << std::endl;
return -1;
} else {
std::cout << "process " << pid << " is running!" << std::endl;
}
// 根据进程名查询所有的pids
auto tup = ProcessHelper::getAllProcessPidsByName(processName);
auto pids = std::get<0>(tup);
// 杀掉所有的同名进程
for (auto pid : pids) {
std::cout << "killing " << pid << std::endl;
ProcessHelper::killProcess(pid, false);
if (0 != ProcessHelper::isRunning(pid)) std::cout << "process " << pid << " killed!" << std::endl;
}
return 0;
}