如何从32位流程中获取可靠的内存使用信息以进行64位过程



我的目标是获取任意过程的内存使用信息。我从32位过程中进行以下操作:

HANDLE hProc = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, 0, pid);
if(hProc)
{
    PROCESS_MEMORY_COUNTERS_EX pmx = {0};
    if(::GetProcessMemoryInfo(hProc, (PROCESS_MEMORY_COUNTERS*)&pmx, sizeof(pmx)))
    {
        wprintf(L"Working set: %.02f MBn", pmx.WorkingSetSize / (1024.0 * 1024.0));
        wprintf(L"Private bytes: %.02f MBn", pmx.PrivateUsage / (1024.0 * 1024.0));
    }
    ::CloseHandle(hProc);
}

问题是,如果pid进程是一个64位进程,则它可能分配了超过4GB的内存,这将溢出pmx.WorkingSetSizepmx.PrivateUsage,它们在32位进程中都是32位变量。因此,在这种情况下,GetProcessMemoryInfoUINT_MAX返回两个指标,而不是失败,这是错误的!

所以我想知道,是否有可靠的API可以从32位应用程序中的任意过程中检索内存使用?

有一个可靠的API称为"性能数据帮助者"。

Windows的库存perfmon实用程序是Windows Performance Counter应用程序的经典示例。同样,Process Explorer正在使用它来收集流程统计。

它的优势是您甚至不需要SeDebugPrivilege来获得PROCESS_VM_READ访问其他流程。
请注意,尽管访问仅限于用户参与性能监视用户组的一部分。

PDH背后的想法是:

  • 查询对象
    • 一个或多个计数器
  • 根据要求或定期创建样本
  • 获取您要求的数据

让您开始工作要做更多的工作,但最终仍然很容易。我要做的是设置一个永久的PDH查询,以便我可以在应用程序的整个生命周期中重复使用。

有一个缺点:默认情况下,操作系统为具有相同名称的进程创建编号条目。这些编号的条目甚至在进程终止或创建新的条目时发生了变化。因此,您必须对此进行解释并交叉检查流程ID(PID),实际上,在要获得的过程(ES)时,您想获得的内存使用量。

在下面,您找到了GetProcessMemoryInfo()的简单PDH包装器替代品。当然,有足够的空间来调整以下代码或根据您的需求进行调整。我还看到已经创建了更多通用C 包装的人。

声明

#include <tuple>
#include <array>
#include <vector>
#include <stdint.h>
#include <Pdh.h>
#pragma comment(lib, "Pdh.lib")

class process_memory_info
{
private:
    using pd_t = std::tuple<DWORD, ULONGLONG, ULONGLONG>; // performance data type
    static constexpr size_t pidIdx = 0;
    static constexpr size_t wsIdx = 1;
    static constexpr size_t pbIdx = 2;
    struct less_pd
    {
        bool operator ()(const pd_t& left, const pd_t& right) const
        {
            return std::get<pidIdx>(left) < std::get<pidIdx>(right);
        }
    };
public:
    ~process_memory_info();
    bool setup_query();
    bool take_sample();
    std::pair<uintmax_t, uintmax_t> get_memory_info(DWORD pid) const;
private:
    PDH_HQUERY pdhQuery_ = nullptr;
    std::array<PDH_HCOUNTER, std::tuple_size_v<pd_t>> pdhCounters_ = {};
    std::vector<pd_t> perfData_;
};

实施

#include <memory>
#include <execution>
#include <algorithm>
#include <stdlib.h>
using std::unique_ptr;
using std::pair;
using std::array;
using std::make_unique;
using std::get;

process_memory_info::~process_memory_info()
{
    PdhCloseQuery(pdhQuery_);
}
bool process_memory_info::setup_query()
{
    if (pdhQuery_)
        return true;
    if (PdhOpenQuery(nullptr, 0, &pdhQuery_))
        return false;
    size_t i = 0;
    for (auto& counterPath : array<PDH_COUNTER_PATH_ELEMENTS, std::tuple_size_v<pd_t>>{ {
        { nullptr, L"Process", L"*", nullptr, 0, L"ID Process" },
        { nullptr, L"Process", L"*", nullptr, 0, L"Working Set" },
        { nullptr, L"Process", L"*", nullptr, 0, L"Private Bytes" }
        }})
    {
        wchar_t pathStr[PDH_MAX_COUNTER_PATH] = {};
        DWORD size;
        PdhMakeCounterPath(&counterPath, pathStr, &(size = _countof(pathStr)), 0);
        PdhAddEnglishCounter(pdhQuery_, pathStr, 0, &pdhCounters_[i++]);
    }
    return true;
}
bool process_memory_info::take_sample()
{
    if (PdhCollectQueryData(pdhQuery_))
        return false;
    DWORD nItems = 0;
    DWORD size;
    PdhGetFormattedCounterArray(pdhCounters_[0], PDH_FMT_LONG, &(size = 0), &nItems, nullptr);
    auto valuesBuf = make_unique<BYTE[]>(size);
    PdhGetFormattedCounterArray(pdhCounters_[0], PDH_FMT_LONG, &size, &nItems, PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.get()));
    unique_ptr<PDH_FMT_COUNTERVALUE_ITEM[]> pidValues{ PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.release()) };
    valuesBuf = make_unique<BYTE[]>(size);
    PdhGetFormattedCounterArray(pdhCounters_[1], PDH_FMT_LARGE, &size, &nItems, PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.get()));
    unique_ptr<PDH_FMT_COUNTERVALUE_ITEM[]> wsValues{ PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.release()) };
    valuesBuf = make_unique<BYTE[]>(size);
    PdhGetFormattedCounterArray(pdhCounters_[2], PDH_FMT_LARGE, &size, &nItems, PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.get()));
    unique_ptr<PDH_FMT_COUNTERVALUE_ITEM[]> pbValues{ PPDH_FMT_COUNTERVALUE_ITEM(valuesBuf.release()) };
    perfData_.clear();
    perfData_.reserve(nItems);
    for (size_t i = 0, n = nItems; i < n; ++i)
    {
        perfData_.emplace_back(pidValues[i].FmtValue.longValue, wsValues[i].FmtValue.largeValue, pbValues[i].FmtValue.largeValue);
    }
    std::sort(std::execution::par_unseq, perfData_.begin(), perfData_.end(), less_pd{});
    return true;
}
pair<uintmax_t, uintmax_t> process_memory_info::get_memory_info(DWORD pid) const
{
    auto it = std::lower_bound(perfData_.cbegin(), perfData_.cend(), pd_t{ pid, 0, 0 }, less_pd{});
    if (it != perfData_.cend() && get<pidIdx>(*it) == pid)
        return { get<wsIdx>(*it), get<pbIdx>(*it) };
    else
        return {};
}

int main()
{
    process_memory_info pmi;
    pmi.setup_query();
    DWORD pid = 4;
    pmi.take_sample();
    auto[workingSet, privateBytes] = pmi.get_memory_info(pid);
    return 0;
}

为什么不将此应用程序编译为64位,然后您应该能够收集32位和64位进程的内存使用量。

WMI WIN32_PROCESS提供商的内存号码有很多。不确定您是否在那里的一切都在那里。

相关内容

最新更新