[英]How to get a reliable memory usage information for a 64-bit process from a 32-bit process?
我的目標是獲取任意進程的內存使用信息。 我從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 MB\n", pmx.WorkingSetSize / (1024.0 * 1024.0));
wprintf(L"Private bytes: %.02f MB\n", pmx.PrivateUsage / (1024.0 * 1024.0));
}
::CloseHandle(hProc);
}
問題是,如果pid
進程是64位進程,則它可能分配了超過4GB的內存,這將使pmx.WorkingSetSize
和pmx.PrivateUsage
都溢出,它們都是32位進程中的32位變量。 。 因此,在這種情況下, GetProcessMemoryInfo
不會失敗,而是將兩個指標都返回為UINT_MAX
,這是錯誤的!
所以我想知道,是否有可靠的API從32位應用程序中的任意進程中檢索內存使用情況?
有一個可靠的API,稱為“ Performance Data Helpers ”。
Windows的stock perfmon
實用程序是Windows Performance Counter應用程序的經典示例。 Process Explorer還將其用於收集過程統計信息。
它的優點是您甚至不需要SeDebugPrivilege
即可獲得對其他進程的PROCESS_VM_READ
訪問權限。
請注意,盡管訪問僅限於屬於Performance Monitoring Users組的用戶。
PDH背后的想法是:
使您入門還需要做更多的工作,但最終仍然很容易。 我要做的是建立一個永久性的PDH查詢,這樣我就可以在應用程序的整個生命周期中重用它。
有一個缺點:默認情況下,操作系統為具有相同名稱的進程創建編號的條目。 這些編號的條目甚至在進程終止或創建新條目時更改。 因此,您必須考慮到這一點並交叉檢查進程ID(PID),實際上是在打開要獲取其內存使用量的進程的句柄的同時。
在下面,您可以找到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提供程序有很多64位內存號。 不知道您所追求的一切是否存在。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.