从C和C ++的父进程获取子进程的列表(跨平台,无命令行)

时间:2020-06-13 21:52:01

标签: c++ c cross-platform desktop pid

如何在C和C ++中以跨平台方式从给定的父进程ID获取子进程ID的列表,而不使用命令行?我在下面提供了一个答案,其中涉及使用C ++的Win32,macOS,Linux,FreeBSD和Darwin。

可以随意将我的代码转换为C解决方案(以及根据需要提供的本机api或posix),或使用其他API或方法提供自己的解决方案,但无需使用popen()或system()之类的东西。添加对更多平台的支持显然也很受欢迎。

例如:其他BSD,Solaris,移动平台等。

1 个答案:

答案 0 :(得分:4)

Win32

#include "childpids.h"

#include <windows.h>
#include <tlhelp32.h>

using std::string;
using std::to_string;

string pids_from_ppid_helper(process_t ppid) {
  string pids;
  HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  PROCESSENTRY32 pe = { 0 };
  pe.dwSize = sizeof(PROCESSENTRY32);
  if (Process32First(hp, &pe)) {
    do {
      if (pe.th32ParentProcessID == ppid) {
        pids += to_string(pe.th32ProcessID) + "|";
      }
    } while (Process32Next(hp, &pe));
  }
  if (pids.back() == '|')
    pids.pop_back();
  pids += "\0";
  CloseHandle(hp);
  return pids;
}

macOS和Darwin

#include <algorithm>
#include <vector>

#include <cstring>

#include "childpids.h"

#include <sys/proc_info.h>
#include <libproc.h>
    
using std::string;
using std::vector;
using std::to_string;

static inline process_t ppid_from_pid(process_t pid) {
  process_t ppid;
  proc_bsdinfo proc_info;
  if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc_info, sizeof(proc_info)) > 0) {
    ppid = proc_info.pbi_ppid;
  }
  return ppid;
}

string pids_from_ppid_helper(process_t ppid) {
  string pids;
  int cntp = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
  vector<pid_t> proc_info(cntp);
  std::fill(proc_info.begin(), proc_info.end(), 0);
  proc_listpids(PROC_ALL_PIDS, 0, &proc_info[0], sizeof(pid_t) * cntp);
  for (unsigned i = 0; i < cntp; i++) {
    if (proc_info[i] == 0) { continue; }
    if (ppid_from_pid(proc_info[i]) == ppid) {
      pids += to_string(proc_info[i]) + "|";
    }
  }
  if (pids.back() == '|')
    pids.pop_back();
  pids += "\0";
  return pids;
}

Linux(与-lprocps链接)

// Note: Ubuntu/Debian need to install libprocps-dev for the development headers.
// All major Linux distros *should* have the actual library installed by default.

#include <cstring>

#include "childpids.h"

#include <proc/readproc.h>
    
using std::string;
using std::to_string;

string pids_from_ppid_helper(process_t ppid) {
  string pids;
  proc_t proc_info;
  memset(&proc_info, 0, sizeof(proc_info));
  PROCTAB *proc = openproc(PROC_FILLMEM | PROC_FILLSTAT | PROC_FILLSTATUS);
  while (readproc(proc, &proc_info) != 0) {
    if (proc_info.ppid == ppid) {
      pids += to_string(proc_info.tgid) + "|";
    }
  }
  if (pids.back() == '|')
    pids.pop_back();
  pids += "\0";
  closeproc(proc);
  return pids;
}

FreeBSD(链接:-lutil -lc)

// Note: libutil.h is specific to the FreeBSD BSD distro.
// For more BSD support, call sysctl() function directly.

#include <cstdlib>

#include "childpids.h"

#include <sys/types.h>
#include <sys/user.h>

#include <libutil.h>

using std::string;
using std::to_string;

string pids_from_ppid_helper(process_t ppid) {
  string pids; int cntp;
  struct kinfo_proc *proc_info = kinfo_getallproc(&cntp);
  if (proc_info) {
    for (unsigned i = 0; i < cntp; i++) {
      if (proc_info[i].ki_ppid == ppid) {
        pids += to_string(proc_info[i].ki_pid) + "|";
      }
    }
  }
  if (pids.back() == '|')
    pids.pop_back();
  pids += "\0";
  free(proc_info);
  return pids;
}

childpids.h

#include <string>

// pid_t is signed, but only returns negative for fork() errors
// we are not using posix fork() anywhere, so this should be ok
typedef unsigned long process_t;

std::string pids_from_ppid_helper(process_t ppid);

childpids.cpp

#include <iostream>
#include <sstream>
#include <vector>
#include <string>

#include "childpids.h"
    
using std::string;

static inline std::vector<string> string_split(string str, char delimiter) {
  std::vector<string> vec;
  std::stringstream sstr(str);
  string tmp;
  while (std::getline(sstr, tmp, delimiter))
    vec.push_back(tmp);
  return vec;
}

void pids_from_ppid(process_t ppid) {
  string pids = pids_from_ppid_helper(ppid);
  std::vector<string> pidVec = string_split(pids, '|');
  for (const string &pid : pidVec) {
    if (pid != "" && pid != "0") {
      pids_from_ppid(stoul(pid, nullptr, 10)); // recursive
      std::cout << pid << std::endl; // show children first
    }
  }
}

int main(int argc, char *argv[]) {
  string arg = (argc == 2) ? argv[1] : "0";
  pids_from_ppid(stoul(arg, nullptr, 10));
  return 0;
}