C++基于Boost.Process封装的进程管理模块

0. 概述

Boost.Process提供了强大的跨平台进程管理能力,本文将介绍如何利用Boost.Process库,封装一个简化且高效的进程管理模块。该模块旨在屏蔽底层库的复杂性,提供简洁易用的API,支持Windows和Linux平台,同时兼容x86和ARM架构。

1. 设计目标

  1. 简化封装: 提供高级别的进程管理接口,隐藏Boost.Process底层细节。
  2. 跨平台支持: 确保在Windows和Linux下均稳定运行,支持多种硬件架构。

2. 实现详解

2.1 进程管理函数详解

  1. getTimeStamp(): 获取当前时间戳,用于计算进程运行时间。
  2. getCurrentProcessId(): 跨平台获取当前进程ID的实现。
  3. isRunning(): 检查指定进程ID的进程是否在运行。
    • Windows: 使用Toolhelp32Snapshot API。
    • Linux: 通过读取/proc/[pid]/status文件检查进程状态。
  4. killProcess(): 终止指定进程ID的进程。
    • Windows和Linux: 使用不同的方法终止进程,支持终止子进程和非子进程。
  5. getAllProcessPidsByName(): 根据进程名获取所有匹配的进程ID列表。
    • Windows: 使用Toolhelp32Snapshot获取进程列表。
    • Linux: 遍历/proc目录下的进程状态文件,匹配进程名。
  6. 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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值