c++ - Windows中如何获取当前进程的所有子进程的句柄?

标签 c++ winapi time process children

为了在 Windows 操作系统上进行性能监控,我需要一个可以报告任意进程的用户和内核时间的程序。在 POSIX 系统上,标准的 time 实用程序完全可以,因为它会报告挂钟时间、用户时间和内核时间。

对于 Windows,默认情况下没有这样的实用程序。我环顾四周,发现至少三种选择。正如我在下面解释的那样,它们实际上都不适合我的需要。

  1. timeit 来自 Windows SDK(不记得确切的版本)。它不再分发、支持或保证在现代系统上工作。我无法对其进行测试。
  2. Cygwin 的时间。几乎与具有相似输出格式的 POSIX 对应物相同。
  3. Johnson (John) Hart 的
  4. timep.exe,在 source code 中可用和他的书“Windows 系统编程,第 4 版”的二进制文件。这是一个非常简单的实用程序,它使用 WinAPI 的 GetProcessTimes() 来获取完全相同的三个值。我怀疑 Cygwin 的 time 在这方面没有什么不同。

现在的问题是:GetProcessTimes() 只报告由 timep 直接生成的 PID 的时间,不是它的子级。这使得 timetimep 对我来说都毫无用处。

我的目标 EXE 应用程序通常是通过调用另一个 BAT 文件的 BAT 文件生成的;两个 BAT 都用于调整环境或更改命令行参数:

timep.exe
|    
+---wrapper.bat
              |
              +--- real-wrapper.bat
                                  |
                                  +--- application.exe

wrapper.bat 单独报告的时间与 application.exe 无关。 显然,POSIX(fork-exec)和Win32(CreateProcess)的进程创建模型有很大的不同,这使得我的目标在Windows上很难实现。

我想尝试编写我自己的time 变体。它必须递归地总结给定进程及其所有子进程、孙进程等的时间。到目前为止,我可以想象以下方法:

  1. CreateProcess() 并获取其 PID(根 PID)并处理;将此句柄添加到列表
  2. 枚举系统中的所有进程;对于每个过程
    1. 将其 PID 与根 PID 进行比较。如果相等,获取PID并为其处理,添加到句柄列表中。
    2. 对于每个新的 PID,重复进程扫描阶段以收集更多子句柄
    3. 递归直到没有新的进程句柄被添加到列表中
  3. 等待列表中所有收集到的句柄终止。
  4. 对于每个句柄,调用 GetProcessTimes() 并对它们求和
  5. 报告结果

这个算法不好,因为它是活泼的——子进程可能在任何进程的生命周期后期创建,或者它们可以在我们有机会获得它们的句柄之前终止。在这两种情况下,报告的结果时间都是不正确的。

我的问题是:有更好的解决方案吗?


编辑:我能够通过使用 Job Objects 实现我的目标.下面是从我的应用程序中提取的代码片段,与从进程及其所有子进程中获取内核和用户时间有关。希望它能为某人节省一些时间。

我在 Windows 8.1 x64 和 VS 2015 上对其进行了测试,但它应该至少可以向后移植到 Windows 7。关于 long,32 位主机(我不确定)可能需要一些摆弄long 类型 - 我不熟悉 CL.EXE 在此类平台上处理它们的方式。

#include <windows.h>
#include <string>
#include <cassert>
#include <iostream>
/* ... */

STARTUPINFO startUp;
PROCESS_INFORMATION procInfo;


/* Start program in paused state */
PROCESS_INFORMATION procInfo;
if (!CreateProcess(NULL, CmdParams, NULL, NULL, TRUE,
    CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS, NULL, NULL, &startUp, &procInfo)) {
    DWORD err = GetLastError();
    // TODO format error message
    std::cerr << "Unable to start the process: " << err << std::endl;
    return 1;
}

HANDLE hProc = procInfo.hProcess;

/* Create job object and attach the process to it */
HANDLE hJob = CreateJobObject(NULL, NULL); // XXX no security attributes passed
assert(hJob != NULL);
int ret = AssignProcessToJobObject(hJob, hProc);
assert(ret);

/* Now run the process and allow it to spawn children */
ResumeThread(procInfo.hThread);

/* Block until the process terminates */
if (WaitForSingleObject(hProc, INFINITE) != WAIT_OBJECT_0) {
    DWORD err = GetLastError();
    // TODO format error message
    std::cerr << "Failed waiting for process termination: " << err << std::endl;
    return 1;
}

DWORD exitcode = 0;
ret = GetExitCodeProcess(hProc, &exitcode);
assert(ret);

/* Calculate wallclock time in nanoseconds.
   Ignore user and kernel times (third and fourth return parameters) */
FILETIME createTime, exitTime, unusedTime;
ret = GetProcessTimes(hProc, &createTime, &exitTime, &unusedTime, &unusedTime);
assert(ret);

LONGLONG createTimeNs = (LONGLONG)createTime.dwHighDateTime << 32 | createTime.dwLowDateTime;
LONGLONG exitTimeNs = (LONGLONG)exitTime.dwHighDateTime << 32 | exitTime.dwLowDateTime;
LONGLONG wallclockTimeNs = exitTimeNs - createTimeNs;

/* Get total user and kernel times for all processes of the job object */
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jobInfo;
ret = QueryInformationJobObject(hJob, JobObjectBasicAccountingInformation,
    &jobInfo, sizeof(jobInfo), NULL);
assert(ret);

if (jobInfo.ActiveProcesses != 0) {
    std::cerr << "Warning: there are still " 
        << jobInfo.ActiveProcesses 
        << " alive children processes" << std::endl;
    /* We may kill survived processes, if desired */
    TerminateJobObject(hJob, 127);
}

/* Get kernel and user times in nanoseconds */
LONGLONG kernelTimeNs = jobInfo.TotalKernelTime.QuadPart;
LONGLONG userTimeNs = jobInfo.TotalUserTime.QuadPart;

/* Clean up a bit */
CloseHandle(hProc);
CloseHandle(hJob);

最佳答案

是的,从 timep.exe 创建一个作业,并使用 job accounting .子进程(除非在它们自己的作业中创建)与其父进程共享作业。

这几乎跳过了您的第 2-4 步

关于c++ - Windows中如何获取当前进程的所有子进程的句柄?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36011572/

相关文章:

c# - C# 中的时间相关着色

c++ - 将 C++ 项目升级到 VS2010,现在出现 AccessViolationException

winapi - WinLockDLL.dll在Windows7中的函数

MySQL:当我已经有时间戳字段时,我应该使用 "date"和 "time"字段吗?

c++ - 需要检索用户所属的所有组......在 C++ 中

c - 在 C + Win API 中编程 : How To Get Windows 7 Look For Controls?

MySQL记录时间戳发生变化

c++ - 访问仿函数中的 using 声明 : a scope related question

c++ - 未定义引用 [USRP] [UHD]

c++ - 为什么模板参数推导失败?