C++에서 성능 데이터 액세스
WMI 고성능 API는 성능 카운터 클래스에서 데이터를 가져오는 일련의 인터페이스입니다. 이 인터페이스가 샘플링 속도를 높이려면 리프레셔 개체를 사용해야 합니다. 스크립팅에서 리프레셔 개체를 사용하는 방법에 대한 자세한 내용은 스크립트에서 성능 데이터 액세스 및 WMI 작업: 성능 모니터링을 참조하세요.
이 항목에서 다루는 섹션은 다음과 같습니다.
성능 데이터 새로 고침
리프레셔 개체는 프로세스 경계를 넘지 않는 상태로 데이터를 검색하여 데이터 공급자 및 클라이언트의 성능을 향상합니다. 클라이언트와 서버가 동일한 컴퓨터에 있는 경우 리프레셔는 프로세스 내에서 고성능 공급자를 클라이언트에 로드하고 공급자 개체에서 클라이언트 개체로 직접 데이터를 복사합니다. 클라이언트와 서버가 서로 다른 컴퓨터에 있는 경우 리프레셔는 원격 컴퓨터에 개체를 캐싱하고 최소한의 데이터 세트를 클라이언트로 전송하여 성능을 향상합니다.
리프레셔는 다음 작업도 수행합니다.
네트워크 오류가 발생하거나 원격 컴퓨터가 다시 시작되면 클라이언트를 원격 WMI 서비스에 자동으로 다시 연결합니다.
두 컴퓨터 간의 원격 연결이 실패한 경우 리프레셔는 기본적으로 애플리케이션을 관련 고성능 공급자에 다시 연결하려고 시도합니다. 재연결을 방지하려면 Refresh 메서드 호출에서 WBEM_FLAG_REFRESH_NO_AUTO_RECONNECT 플래그를 전달합니다. 스크립팅 클라이언트는 SWbemRefresher.AutoReconnect 속성을 FALSE로 설정해야 합니다.
동일한 공급자나 다른 공급자가 제공하는 여러 개체와 열거자를 로드합니다.
여러 개체, 열거자 또는 둘 다를 리프레셔에 추가할 수 있도록 지원합니다.
개체를 열거합니다.
고성능 공급자는 다른 공급자와 마찬가지로 개체를 열거할 수 있습니다.
고성능 클라이언트의 작성을 완료한 후에는 응답 시간을 개선해야 할 수 있습니다. IWbemObjectAccess 인터페이스는 속도에 최적화되어 있으므로 본질적으로 스레드세이프가 아닙니다. 따라서 새로 고침 작업 중에는 새로 고칠 수 있는 개체 또는 열거에 액세스하지 않아야 합니다. IWbemObjectAccess 메서드 호출 중에 스레드에 있는 모든 개체를 보호하려면 IWbemObjectAccess::Lock 및 Unlock 메서드를 사용합니다. 성능을 개선하려면 개별 스레드를 잠글 필요가 없도록 스레드를 동기화합니다. 스레드를 줄이고 새로 고침 작업을 위해 개체 그룹을 동기화하면 전반적으로 가장 높은 성능을 얻을 수 있습니다.
WMI 리프레셔에 열거자 추가
인스턴스의 개수와 각 인스턴스의 데이터는 IWbemRefresher::Refresh가 호출될 때마다 완전한 열거가 생성되도록 리프레셔에 열거자를 추가하여 새로 고쳐집니다.
다음 C++ 코드 예제가 올바르게 컴파일되려면 다음 참조 및 #include 문이 필요합니다.
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
다음 절차에서는 리프레셔에 열거자를 추가하는 방법을 보여 줍니다.
리프레셔에 열거자를 추가하려면 다음을 수행합니다.
새로 고칠 수 있는 개체의 경로와 IWbemServices 인터페이스를 사용하여 IWbemConfigureRefresher::AddEnum 메서드를 호출합니다.
리프레셔는 IWbemHiPerfEnum 인터페이스에 대한 포인터를 반환합니다. IWbemHiPerfEnum 인터페이스를 사용하여 열거에 있는 개체에 액세스할 수 있습니다.
IWbemHiPerfEnum* pEnum = NULL; long lID; IWbemConfigureRefresher* pConfig; IWbemServices* pNameSpace; // Add an enumerator to the refresher. if (FAILED (hr = pConfig->AddEnum( pNameSpace, L"Win32_PerfRawData_PerfProc_Process", 0, NULL, &pEnum, &lID))) { goto CLEANUP; } pConfig->Release(); pConfig = NULL;
다음 작업을 수행하는 루프를 만듭니다.
IWbemRefresher::Refresh를 호출하여 개체를 새로 고칩니다.
IWbemHiPerfEnum::GetObjects 메서드에 대한 IWbemObjectAccess 인터페이스 포인터로 구성된 배열을 제공합니다.
GetObjects로 전달된 IWbemObjectAccess 메서드를 사용하여 열거자의 속성에 액세스합니다.
각 IWbemObjectAccess 인스턴스에 속성 핸들을 전달하여 새로 고쳐진 값을 가져올 수 있습니다. 클라이언트는 Release를 호출하여 GetObjects에 의해 반환된 IWbemObjectAccess 포인터를 해제해야 합니다.
예
다음 C++ 코드 예제에서는 클라이언트가 첫 번째 개체의 속성 핸들을 가져온 다음 새로 고침 작업의 나머지 부분에서 이 핸들을 재사용하는 고성능 클래스를 열거합니다. 각 Refresh 메서드 호출은 인스턴스의 개수와 인스턴스 데이터를 업데이트합니다.
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
int __cdecl wmain(int argc, wchar_t* argv[])
{
// To add error checking,
// check returned HRESULT below where collected.
HRESULT hr = S_OK;
IWbemRefresher *pRefresher = NULL;
IWbemConfigureRefresher *pConfig = NULL;
IWbemHiPerfEnum *pEnum = NULL;
IWbemServices *pNameSpace = NULL;
IWbemLocator *pWbemLocator = NULL;
IWbemObjectAccess **apEnumAccess = NULL;
BSTR bstrNameSpace = NULL;
long lID = 0;
long lVirtualBytesHandle = 0;
long lIDProcessHandle = 0;
DWORD dwVirtualBytes = 0;
DWORD dwProcessId = 0;
DWORD dwNumObjects = 0;
DWORD dwNumReturned = 0;
DWORD dwIDProcess = 0;
DWORD i=0;
int x=0;
if (FAILED (hr = CoInitializeEx(NULL,COINIT_MULTITHREADED)))
{
goto CLEANUP;
}
if (FAILED (hr = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_NONE,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, 0)))
{
goto CLEANUP;
}
if (FAILED (hr = CoCreateInstance(
CLSID_WbemLocator,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
(void**) &pWbemLocator)))
{
goto CLEANUP;
}
// Connect to the desired namespace.
bstrNameSpace = SysAllocString(L"\\\\.\\root\\cimv2");
if (NULL == bstrNameSpace)
{
hr = E_OUTOFMEMORY;
goto CLEANUP;
}
if (FAILED (hr = pWbemLocator->ConnectServer(
bstrNameSpace,
NULL, // User name
NULL, // Password
NULL, // Locale
0L, // Security flags
NULL, // Authority
NULL, // Wbem context
&pNameSpace)))
{
goto CLEANUP;
}
pWbemLocator->Release();
pWbemLocator=NULL;
SysFreeString(bstrNameSpace);
bstrNameSpace = NULL;
if (FAILED (hr = CoCreateInstance(
CLSID_WbemRefresher,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWbemRefresher,
(void**) &pRefresher)))
{
goto CLEANUP;
}
if (FAILED (hr = pRefresher->QueryInterface(
IID_IWbemConfigureRefresher,
(void **)&pConfig)))
{
goto CLEANUP;
}
// Add an enumerator to the refresher.
if (FAILED (hr = pConfig->AddEnum(
pNameSpace,
L"Win32_PerfRawData_PerfProc_Process",
0,
NULL,
&pEnum,
&lID)))
{
goto CLEANUP;
}
pConfig->Release();
pConfig = NULL;
// Get a property handle for the VirtualBytes property.
// Refresh the object ten times and retrieve the value.
for(x = 0; x < 10; x++)
{
dwNumReturned = 0;
dwIDProcess = 0;
dwNumObjects = 0;
if (FAILED (hr =pRefresher->Refresh(0L)))
{
goto CLEANUP;
}
hr = pEnum->GetObjects(0L,
dwNumObjects,
apEnumAccess,
&dwNumReturned);
// If the buffer was not big enough,
// allocate a bigger buffer and retry.
if (hr == WBEM_E_BUFFER_TOO_SMALL
&& dwNumReturned > dwNumObjects)
{
apEnumAccess = new IWbemObjectAccess*[dwNumReturned];
if (NULL == apEnumAccess)
{
hr = E_OUTOFMEMORY;
goto CLEANUP;
}
SecureZeroMemory(apEnumAccess,
dwNumReturned*sizeof(IWbemObjectAccess*));
dwNumObjects = dwNumReturned;
if (FAILED (hr = pEnum->GetObjects(0L,
dwNumObjects,
apEnumAccess,
&dwNumReturned)))
{
goto CLEANUP;
}
}
else
{
if (hr == WBEM_S_NO_ERROR)
{
hr = WBEM_E_NOT_FOUND;
goto CLEANUP;
}
}
// First time through, get the handles.
if (0 == x)
{
CIMTYPE VirtualBytesType;
CIMTYPE ProcessHandleType;
if (FAILED (hr = apEnumAccess[0]->GetPropertyHandle(
L"VirtualBytes",
&VirtualBytesType,
&lVirtualBytesHandle)))
{
goto CLEANUP;
}
if (FAILED (hr = apEnumAccess[0]->GetPropertyHandle(
L"IDProcess",
&ProcessHandleType,
&lIDProcessHandle)))
{
goto CLEANUP;
}
}
for (i = 0; i < dwNumReturned; i++)
{
if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
lVirtualBytesHandle,
&dwVirtualBytes)))
{
goto CLEANUP;
}
if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
lIDProcessHandle,
&dwIDProcess)))
{
goto CLEANUP;
}
wprintf(L"Process ID %lu is using %lu bytes\n",
dwIDProcess, dwVirtualBytes);
// Done with the object
apEnumAccess[i]->Release();
apEnumAccess[i] = NULL;
}
if (NULL != apEnumAccess)
{
delete [] apEnumAccess;
apEnumAccess = NULL;
}
// Sleep for a second.
Sleep(1000);
}
// exit loop here
CLEANUP:
if (NULL != bstrNameSpace)
{
SysFreeString(bstrNameSpace);
}
if (NULL != apEnumAccess)
{
for (i = 0; i < dwNumReturned; i++)
{
if (apEnumAccess[i] != NULL)
{
apEnumAccess[i]->Release();
apEnumAccess[i] = NULL;
}
}
delete [] apEnumAccess;
}
if (NULL != pWbemLocator)
{
pWbemLocator->Release();
}
if (NULL != pNameSpace)
{
pNameSpace->Release();
}
if (NULL != pEnum)
{
pEnum->Release();
}
if (NULL != pConfig)
{
pConfig->Release();
}
if (NULL != pRefresher)
{
pRefresher->Release();
}
CoUninitialize();
if (FAILED (hr))
{
wprintf (L"Error status=%08x\n",hr);
}
return 1;
}
관련 항목