Счётчики производительности

Часть 1. Чтение

Автор: Сергей Холодилов
The RSDN Group

Источник: RSDN Magazine #4-2003
Опубликовано: 24.02.2004
Версия текста: 1.0
Общая картина
Структура данных о производительности
Содержимое данных о производительности
База имён
Удалённый доступ
Параметр lpValueName функции RegQueryValueEx
All together now
Код
PCViewer
Processor Protector

One, two, three, four,
Can I have a little more?
J.Lennon

Демонстрационный пример

«Счётчики производительности» (perfomance counters) – это расширяемый механизм сбора различной (в основном статистической) информации, заложенный в операционные системы линейки Windows NT, начиная с версии 3.1. Большая часть счётчиков доступна пользователю через оснастку (snap-in) Performance. О том, сколько всего интересного можно узнать, используя счётчики производительности, говорит тот факт, что примерно половина экспериментов, описанных в книге «Внутреннее устройство Windows 2000» Д.Соломона и М.Руссиновича, проводилась с использованием оснастки Performance. Кстати сказать, возможно чтение счётчиков и с удалённого компьютера.

Это мощный и гибкий механизм. Но, на мой взгляд, он неочевиден, громоздок и неудобен. Широкое практическое применение программистами представляется мне сомнительным. Однако есть несколько ситуаций, когда использование счётчиков производительности может быть оправдано:

Я считаю, что создание собственных счётчиков более интересно и полезно, но и более трудоёмко. Поэтому первая часть этой статьи описывает чтение данных существующих счётчиков производительности (всё равно считывать данные со счётчиков, хотя бы и своих, придётся) и морально подготавливает к созданию собственных счётчиков. Вторая посвящена собственно созданию.

Общая картина

Для получения значений счётчиков производительности нужно сделать примерно такой вызов:

res = RegQueryValueEx(
  HKEY_PERFORMANCE_DATA, 
  «...», 
  0, &type, 
  buffer, &size);
ПРИМЕЧАНИЕ

Для доступа к счётчикам производительности существует несколько интерфейсов. Я знаю о четырёх: функция RegQueryValueEx, библиотека PDH (Performance Data Helper), WMI (Windows Management Instrumentation) и ActiveX System Monitor (если его можно назвать интерфейсом).

В статье рассматривается только интерфейс RegQueryValueEx – базовый и наиболее простой (всего одна функция, не вводятся дополнительные уровни абстракции). Остальные интерфейсы удобнее, но, во-первых, сложнее, во-вторых, их изучение никак не поможет при создании собственных счётчиков производительности.

Особенности, отличающие этот код от обычного использования RegQueryValueEx:

Но главное отличие – это, конечно, возвращаемые данные.

Структура данных о производительности

Данные, возвращаемые RegQueryValueEx, имеют чёткую иерархию.

ПРИМЕЧАНИЕ

В этом разделе я рассматриваю именно саму иерархию, т.е. уровни и классификацию полученных данных. Не относящиеся к делу поля описываемых структур не приводятся.

Все размеры и смещения даны в байтах. Все смещения отсчитываются от начала соответствующей структуры.

На верхнем уровне находится весь блок данных в целом. Он описывается структурой PERF_DATA_BLOCK:

      typedef
      struct _PERF_DATA_BLOCK 
{
  ...
  // Размер всего блока данных
  DWORD     TotalByteLength; 
  // Размер структуры. Используется как смещение до начала первого «объекта типа».
  DWORD     HeaderLength;  
  // Количество «объектов типа», данные для которых есть в этом блоке.
  DWORD     NumObjectTypes;
  ...
} PERF_DATA_BLOCK, *PPERF_DATA_BLOCK;

На следующем уровне иерархии находятся «объекты типа» (object type; по-моему, их логично было бы называть «классами» или «типами», но обычно их называют объектами; далее и я буду называть их объектами). Они бывают двух видов:

Основное логическое отличие между этими видами объектов заключается в том, что для «одиночных» объектов данные счётчиков относятся непосредственно к объекту, а для «экземплярных» данные относятся к экземпляру.

Объект описывается следующей структурой:

      typedef
      struct _PERF_OBJECT_TYPE 
{
  // Суммарный размер этого объекта, всех его счётчиков, экземпляров и их // данных. Используется как смещение до следующего объекта.
  DWORD TotalByteLength;  
  // Размер этой структуры и следующих за ней определений счётчиков. В случае// «одиночного» объекта это смещение до начала данных, в случае // «экземплярного» объекта – смещение до определения первого экземпляра.
  DWORD DefinitionLength; 
  // Размер этой структуры (смещение до начала определения счётчиков).
  DWORD HeaderLength;   
 ...
  // Количество счётчиков 
  DWORD NumCounters;    
 ...
  // Количество экземпляров. Признак «одиночного» объекта – значение // PERF_NO_INSTANCES (равное -1).
  LONG NumInstances;      
} PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE;

Далее идут «определения счётчиков». Их должно быть PERF_OBJECT_TYPE::NumCounters штук. Каждый счётчик представлен так:

      typedef
      struct _PERF_COUNTER_DEFINITION 
{
  // Размер этого определения счётчика. Т.е. смещение до следующего.
  DWORD ByteLength;
 ...
  // Смещение данных счётчика от начала блока данных объекта (в случае // «одиночного» объекта) или блока данных экземпляра (в случае // «экземплярного» объекта).
  DWORD CounterOffset;
} 
PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION;

После определений счётчиков у «одиночных» объектов находится блок данных. Он состоит из структуры PERF_COUNTER_BLOCK, и собственно значений счётчиков. Структура PERF_COUNTER_BLOCK:

      typedef
      struct _PERF_COUNTER_BLOCK 
{
  // Размер структуры, включая следующие за ней значения счётчиков.
  DWORD ByteLength; 
} 
PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK;

Данные счётчика находятся по смещению PERF_COUNTER_BLOCK::CounterOffset от начала этой структуры.

У «экземплярных» объектов всё несколько сложнее. За определениями счетчиков следуют «определения экземпляров», каждый со своим блоком данных. Блок данных имеет такую же структуру, что и блоки данных «одиночных» объектов. Определение экземпляра:

      typedef
      struct _PERF_INSTANCE_DEFINITION 
{
  DWORD ByteLength; // Размер этой структуры, т.е. смещение от её// начала до блока данных этого экземпляра.
  ...
} PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION;

Всё вместе выглядит примерно так:


Рисунок 1. Весь блок данных в целом.


Рисунок 2. «Одиночный» объект.


Рисунок 3. «Экземплярный» объект.

Содержимое данных о производительности

А теперь рассмотрим те же самые структуры, но с другой точки зрения.

ПРЕДУПРЕЖДЕНИЕ

Некоторые поля я не описываю и даже не привожу, так как структуры и без того большие, а эти поля не кажутся мне хоть сколько-нибудь важными. А иногда мне и не совсем понятно, зачем они нужны... Поэтому после прочтения статьи рекомендую на всякий случай изучить соответствующие разделы MSDN (информации там немного).

      typedef
      struct _PERF_DATA_BLOCK {
  WCHAR      Signature[4];     // Сигнатура «PERF» в Unicode
  ...
  DWORD      Version;          // Версия, в Windows 2000/XP - 1
  DWORD      Revision;         // Ревизия, в Windows 2000/XP - 1
  DWORD      TotalByteLength;  // См. в предыдущем разделе
  DWORD      HeaderLength;     // См. в предыдущем разделе
  DWORD      NumObjectTypes;   // См. в предыдущем разделе
  ...
  SYSTEMTIME SystemTime;       // Системное время на момент получения данных
  ...
  DWORD      SystemNameLength; // Длина имени системы (в байтах)
  DWORD      SystemNameOffset; // Смещение имени системы от начала структуры;// имя хранится в Unicode
} PERF_DATA_BLOCK, *PPERF_DATA_BLOCK;

Кроме имени системы здесь, пожалуй, ничего полезного нет. А имя системы совпадает с именем компьютера, с которого были получены данные, поэтому большого интереса также не представляет. Остальные структуры содержат несколько больше полезной информации.

Объекты:

      typedef
      struct _PERF_OBJECT_TYPE 
{
  DWORD  TotalByteLength;      // См. в предыдущем разделе
  DWORD  DefinitionLength;     // См. в предыдущем разделе
  DWORD  HeaderLength;         // См. в предыдущем разделе
  DWORD  ObjectNameTitleIndex; // Индекс имени объекта в базе имён.
  ...
  DWORD  ObjectHelpTitleIndex; // Индекс описания объекта в базе имён. 
...
  DWORD  NumCounters;          // См. в предыдущем разделе
  ...
  LONG   NumInstances;         // См. в предыдущем разделе
  DWORD  CodePage;             // Кодовая страница. См. примечание // к PERF_INSTANCE_DEFINITION
  ... 
} 
PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE;

Базе имён посвящён следующий раздел. Если кратко, то поля ObjectNameTitleIndex и ObjectHelpTitleIndex позволяют получить текстовое имя объекта и его описание. Описание может оказаться полезным только в том случае, если планируется его выводить, а вот имя можно использовать при поиске нужного объекта (обычно, правда, поступают наоборот – сначала по имени получают индекс, а уже по индексу находят объект).

Счетчики:

      typedef
      struct _PERF_COUNTER_DEFINITION 
{
  DWORD ByteLength;            // См. в предыдущем разделе
  DWORD CounterNameTitleIndex; // Индекс имени счётчика в базе имён.
  ...
  DWORD CounterHelpTitleIndex; // Индекс описания счётчика в базе имён. 
  ...
  DWORD CounterType;           // Тип счётчика. Подробнее ниже.
  DWORD CounterSize;           // В некоторых случаях (зависит от типа) – // размер данных счётчика.
  DWORD CounterOffset;         // См. в предыдущем разделе
} 
PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION;

Самое интересное поле – это, конечно, CounterType. Оно определяет тип информации, содержащейся в счётчике, размер (или механизм вычисления размера) данных счётчика, манипуляции, которые необходимо произвести со значением счётчика перед использованием, и даже форму, в которой его нужно выводить.

В MSDN это поле уклончиво описано как комбинация различных значений (values). Именно значений, а не флагов, так как некоторые из битов этого поля являются флагами, а некоторые – нет. Те, которые «нет», отличаются тем, что логически они исключают друг друга, а их битовые маски пересекаются, поэтому проверять присутствие/отсутствие оператором «&» не получается. Схема такова:

      union CounterTypeMask
{
  DWORD dw;
  struct
  {
    unsignedint reserved1 : 8;
    unsignedint size      : 2; // размер данных счётчикаunsignedint type      : 2; // тип счётчикаunsignedint reserved2 : 4;
    unsignedint subtype   : 4; // подтип счётчикаunsignedint time      : 2; // подтипы таймеровunsignedint calc      : 6; // механизмы вычисления итогового значения, флагиunsignedint display   : 4; // как отображать
  };
};

Я приведу только значения, соответствующие полям size, type, и два значения поля subtype. Остальные менее интересны и более запутанны (некоторые – значительно более запутанны), если хотите, можете посмотреть их в MSDN.

Константа Значение Описание
PERF_SIZE_DWORD 0x00000000 Данные - это DWORD, занимают 4 байта.
PERF_SIZE_LARGE 0x00000100 Данные - это LARGE_INTEGER, занимают 8 байтов.
PERF_SIZE_ZERO 0x00000200 Данных нет, длина - 0 байт.
PERF_SIZE_VARIABLE_LEN 0x00000300 Размер данных определяется полем CounterSize.
Табл.1.Поле size определяет метод определения размера данных счётчика.
Константа Значение Описание
PERF_TYPE_NUMBER 0x00000000 Число .
PERF_TYPE_COUNTER 0x00000400 Счётчик чего-либо.
PERF_TYPE_TEXT 0x00000800 Текст.
PERF_TYPE_ZERO 0x00000С00 Всегда 0.
Табл.2. Поле type определяет интерпретацию данных счётчика.
Константа Значение Описание
PERF_TEXT_UNICODE 0x00000000 Строчка в Unicode.
PERF_TEXT_ASCII 0x00010000 Строчка в ASCII.
Табл.3. Поле subtype определяет подробности интерпретации данных счётчика. Приведены значения только для типа PERF_TYPE_TEXT.

Полноценный анализ поля CounterType в общем виде довольно сложен. Я, например, не только написать его не собрался, но даже и разобрался не со всеми вариантами. Но если вы не собираетесь писать свою оснастку Performance (да ещё и используя исключительно RegQueryValueEx…), то анализ этого поля вам нужно будет произвести только один раз, на этапе разработки приложения. В конце статьи есть пример такого анализа.

ПРИМЕЧАНИЕ

В MSDN описываются не столько флаги/значения (хотя и они тоже), сколько разумные (с точки зрения Microsoft) комбинации. В моей версии MSDN описано 36 различных типов счетчиков. Для каждого приведена формула, позволяющая из «сырых» данных получить понятное пользователю значение. Здесь я их приводить не буду.

Экземпляры:

      typedef
      struct _PERF_INSTANCE_DEFINITION 
{
    DWORD           ByteLength;             // См. в предыдущем разделе
    DWORD           ParentObjectTitleIndex; // Если у этого объекта есть «родитель», // то это индекс имени объекта// родителя в базе имён. 
    DWORD           ParentObjectInstance;   // Номер экземпляра родителя.
    LONG            UniqueID;               // Идентификатор экземпляра или PERF_NO_UNIQUE_ID.
    DWORD           NameOffset;             // Смещение до начала имени экземпляра
    DWORD           NameLength;             // Длина (в байтах) имени экземпляра, 0, // если имени нет.
} PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION;

Поля ParentObjectTitleIndex и ParentObjectInstance позволяют установить отношения «родитель» - «ребёнок» для экземпляров. У экземпляров большинства объектов нет родителей, но, например, для потока (экземпляр объекта «Thread») родителем является его процесс (экземпляр объекта «Process»); для экземпляра объекта «Image» (образ исполняемого модуля) родитель – соответствующий экземпляр объекта «Process Address Space».

Поля UniqueID, NameOffset и NameLength позволяют определить имя экземпляра. Если UniqueID не равно PERF_NO_UNIQUE_ID (определено как -1), то экземпляры именуются по идентификаторам, и UniqueID – это как раз идентификатор. Иначе экземпляры именуются по именам, смещение имени от начала структуры указано в NameOffset, а длина имени – в NameLength.

ПРИМЕЧАНИЕ

И в MSDN, и в заголовочном файле winperf.h о поле NameOffset написано:

Offset from the beginning of this structure to the Unicode name of this instance.

Однако о поле CodePage структуры PERF_OBJECT_TYPE и там, и там написано примерно следующее:

Specifies the code page. This member is zero if the instance strings are in Unicode. Otherwise, this member is the code-page identifier of the instance names.

Я ни разу не встретил ни ненулевого CodePage, ни неюникодного имени. Поэтому где правда – не знаю.

База имён

База имён – это таблица соответствия между индексом имени/описания чего-либо и самим именем/описанием. Точнее, эти базы могут существовать для нескольких языков, причём по две для каждого – одна для имён, вторая для описаний. Хранится всё, как водится, в реестре, большими, слегка структурированными кусками данных... По этому поводу можно только слегка опечалиться.

И база имён, и база описаний находятся в значениях одного ключа реестра. Есть два варианта (они оба работают, вы можете выбирать любой, но если планируется работать под Windows NT 4.0, то посмотрите Q237304 и выберите первый):

  1. Ключ HKEY_PERFORMANCE_DATA, значения «Counter XYZ» для имён и «Explain XYZ» для описаний.
  2. Ключ «HKLM\Software\Microsoft\Windows NT\CurrentVersion\Perflib\XYZ», значения «Counter» и «Help».

Во всех случаях «XYZ» это трёхсимвольная строчка, содержащая «основную часть идентификатора языка» (primary language identifier) в шестнадцатеричном виде. Языком может быть либо английский, либо язык версии (не локализации, а именно версии; локализацию можно поменять из настроек Regional Options, поменять версию гораздо сложнее, я, например, не умею) Windows. Для английского «XYZ» будет «009», для русского «019». При использовании «009» строчки будут английскими, при использовании языка версии могут быть либо локализованными (если создатель соответствующего объекта позаботился об этом, подробнее во второй части статьи), либо английскими.

СОВЕТ

Есть API-функция, которая вроде бы подходит для определения языка версии Windows. Это GetSystemDefaultUILanguage, но она существует только начиная с Windows 2000, да и название у неё подозрительное :)… Для Windows NT 4.0 нужно либо лезть в реестр (куда не скажу, так как не знаю), либо брать язык из VersionInfo какой-нибудь системной dll (смотрите функцию GetFileVersionInfo и её друзей).

Для удалённого компьютера единственный известный мне вариант – реестр. В Windows 2000 и XP нужно смотреть ключ «HKLM\System\CurrentControlSet\Control\Nls\Language», параметр InstallLanguage. Про Windows NT 4.0 – не знаю.

Базы хранятся в значениях типа REG_MULTI_SZ, то есть состоят из большого количества разделённых нулями строк, последняя строка завершается двойным нулём. Формат такой:

Индекс_1
Текст_1
Индекс_2
Текст_2
...

Здесь Индекс_X – строчка с десятичным представлением числа. Последовательные индексы совершенно точно не являются последовательными числами потому, что в базе имён все индексы (кроме первого) чётные, а в базе описаний – нечётные. Но это не значит, что они идут друг за другом через один – между ними могут быть дырки, в общем случае последовательность не обязательно везде возрастающая (скорее всего она будет возрастающей, но это никем не гарантируется).

ПРИМЕЧАНИЕ

Во многих случаях индекс имени чего-либо и индекс соответствующего этому имени описания являются последовательными числами. Возникает соблазн предположить, что это закон. Однако изучение счётчиков объекта «Browser» опровергает такое предположение.

В дополнение к этому, в параметрах «Last Counter» и «Last Help» (тип REG_DWORD) ключа «HKLM\Software\Microsoft\Windows NT\CurrentVersion\Perflib» хранятся максимальные значения индексов, использованных в базах имён и описаний соответственно.

ПРИМЕЧАНИЕ

Совершенно случайно я «откопал» небольшую недокументированную особенность (сомнительной полезности, но тем не менее).

В файле Winreg.h можно обнаружить следующие строчки:

...

#define HKEY_PERFORMANCE_TEXT (( HKEY ) (ULONG_PTR)((LONG)0x80000050) )

#define HKEY_PERFORMANCE_NLSTEXT (( HKEY ) (ULONG_PTR)((LONG)0x80000060) )

...

С описателем HKEY_PERFORMANCE_TEXT все достаточно просто – он эквивалентен ключу «HKLM\Software\Microsoft\Windows NT\CurrentVersion\Perflib\009». Эквивалентен в том смысле, что если этот ключ открыть, получится как раз этот описатель.

С HKEY_PERFORMANCE_NLSTEXT интереснее. В русской версии Windows 2000 никаких неожиданностей не обнаружилось - он эквивалентен ключу «HKLM\…\Perflib\019». А вот в английской – сюрприз! Он эквивалентен тому же «HKLM\…\Perflib\019»! И такой ключ действительно существует. Видимо, это связано с тем, что язык по умолчанию у меня русский. Однако, в английской Windows 2000 в этом ключе никаких данных нет... Поэтому попытки использовать HKEY_PERFORMANCE_NLSTEXT заканчиваются печально. В Windows XP ситуация улучшилась – в английской версии в «HKLM\…\Perflib\019» есть значения «Counter» и «Help», они содержат копии таких же значений из «HKLM\…\Perflib\009».

Удалённый доступ

Поскольку всё хранится в реестре, для получения информации с удалённого компьютера (работающего под управлением Windows NT/2000/XP) можно использовать функцию RegConnectRegistry.

LONG RegConnectRegistry(
  LPCTSTR lpMachineName,
  HKEY hKey,
  PHKEY phkResult);

Параметры:

Параметр Значение
lpMachineName Имя машины. В MSDN написано, что формат имени – «\\name», но просто «name» у меня тоже работало.
hKey Запрашиваемый описатель. Либо HKEY_LOCAL_MACHINE, либо HKEY_USERS, либо HKEY_PERFORMANCE_DATA.
phkResult Описатель, эквивалентный запрашиваемому, но для удалённой машины.

Вот так просто… всё было бы, если бы все пользователи были администраторами на целевой машине… Иногда это не так. В этом случае может помочь Q155363 (HOWTO: Regulate Network Access to the Windows NT Registry), Q146906 (HOWTO: How To Secure Performance Data in Windows NT) и несколько моих дополнений:

Параметр lpValueName функции RegQueryValueEx

Здесь я просто приведу табличку из MSDN. Естественно, с некоторыми комментариями, половина из которых тоже взята из MSDN :)

ПРИМЕЧАНИЕ

Во всех случаях, кроме последнего, данные относятся к тому же компьютеру, что и используемый описатель ключа реестра. То есть, если использовать HKEY_PERFORMANCE_DATA, это будет машина, на которой выполняется программа, а если передавать в RegQueryValueEx результат RegConnectRegistry – машина с которой установлено соединение.

Значение Описание
Global Будут возвращены данные для всех объектов, кроме тех, которые относятся к категории «Costly».
Costly Будут возвращены данные для тех объектов, которые «долго собирать». Процесс может занять секунд пять-десять-пятнадцать. Некоторые объекты включаются как в категорию Global, так и в категорию Costly.
xx yy zz (Разделённые пробелами десятичные представления индексов имён запрашиваемых объектов.) Функция вернёт данные для запрошённых объектов. В некоторых случаях она по собственной инициативе вернёт данные для ещё нескольких объектов. Например, если вы хотите узнать о процессах, то получите процессы, потоки и «Job Object Details» (в Windows 2000). Если же спросить о потоках или «Job Object Details», то получите ту же тройку объектов.Запрашиваемые объекты могут относиться к любой категории.
Foreign <имя системы> [xx yy zz] Ещё один механизм, предназначенный для получения значений счётчиков с удалённого компьютера. О параметре <имя системы> в MSDN написано следующее: «the name of a foreign computer, such as a Novell NetWare server or a UNIX system». Наверное, это правда. Но проверить я не смог, так как под рукой не оказалось ни одного такого сервера. С Windows-серверами этот механизм у меня не заработал. Если параметры xx yy zz не указаны, то будут возращены все счётчики, если указаны, то аналогично предыдущему пункту.
ПРИМЕЧАНИЕ

Строчки «Global» и «Costly» регистро-зависимы.

All together now

Итак, вы хотите получить значение счётчика «Counter» объекта «Object» c компьютера «Computer». Последовательность действий:

ПРИМЕЧАНИЕ

Проще всего читать базу «009», так как она точно есть. Но тогда имя объекта и счётчика должны быть английскими.

ПРИМЕЧАНИЕ

Для стандартных объектов и их счётчиков индексы не изменяются от системы к системе. Поэтому для них этап получения индексов можно пропустить. Единственная проблема – я не нашёл в MSDN упоминания об этом факте…

После получения данных о счетчиках (чтение базы имён в данном случае не считается) ключ HKEY_PERFORMANCE_DATA нужно закрыть. Но желательно делать это пореже. В том смысле, что лучше сделать это один раз в конце, а не каждый раз после обращения к RegQueryValueEx. Причина проста: для получения данных RegQueryValueEx загружает зарегистрированные в реестре dll-и. При закрытии HKEY_PERFORMANCE_DATA они могут быть выгружены, и при следующем запросе их придётся загружать заново.

Код

В качестве примера я написал две утилиты. Первая – скорее полезная, вторая – скорее забавная...

PCViewer

PCViewer –это утилита, позволяющая просматривать значения счётчиков производительности, их имена, типы и т.п. Она позволяет подключаться к другому компьютеру, выбирать используемую базу имён, формировать любые запросы. Выдаёт всю информацию, возвращаемую RegQueryValueEx (включая смещения и размеры).

Для упрощения перебора структур я написал три простых класса. Для работы с базой имён – ещё два. И один для получения информации от RegQueryValueEx. Эти классы сосредоточены в файлах (*.h и *.cpp) perfobjects, titledb (чтобы продемонстрировать все варианты доступа к базам имён, мне пришлось не слабо изуродовать этот класс, в файлах titledb лежит оригинальная версия, в titledb_for_sample – используемая в проекте), perfdata, все они лежат в каталоге Perf. Кроме них, в этом каталоге присутствуют два файла (опять *.h и *.cpp) countrhelpdb. Это обёртка вокруг классов, работающих с базой имён.

Остальное – пользовательский интерфейс, написанный с использованием WTL 3.1

Processor Protector

Следит за загрузкой процессора. Если она больше критической величины, программа убивает первый же процесс, который сможет открыть и убить (системные процессы так открыть обычно не выходит). Естественно, в цикле.

ПРЕДУПРЕЖДЕНИЕ

Осторожно! Программа работает! В результате её работы вы можете потерять данные, получить BSOD (я получал; насколько я понял, получите вы BSOD или нет, зависит от того, что у вас установлено) и ещё какие-нибудь неприятности.

Для получения информации о процессоре и списка процессов используются фиксированные значения индексов – это стандартные объекты, для них можно.

Единственное, что может вызвать затруднение при просмотре кода – вычисление процента загрузки процессора по значению счётчика. Счётчик имеет следующий тип: PERF_SIZE_LARGE | PERF_TYPE_COUNTER | PERF_COUNTER_RATE | PERF_TIMER_100NS | PERF_DELTA_COUNTER | PERF_INVERSE_COUNTER | PERF_DISPLAY_PERSENT. Здесь:

Конечная формула выглядит так:

X = (1 – (value_2 – value1) / (time_2 – time_1) * frequency / 10000000) * 100 %

Где:

Что ж, можно считать, что теперь к созданию собственных счётчиков Вы готовы хотя бы морально :) Продолжение банкета – во второй части статьи.


Эта статья опубликована в журнале RSDN Magazine #4-2003. Информацию о журнале можно найти здесь