» Получение информации о загрузке системы. Win Api. . . Блог программистов


Блог программистов




20088 Янв

Получение информации о загрузке системы.

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

    Итак, приступим. Главная функция с помощью, которой всё программы узнают информацию о загруженности это функция ZwQuerySystemInformation, если программы используют какие либо другие функции, в любом случае всё сводится к этой функции. Это так называемая user mode Native API функция. Приведу её заголовок:

Function ZwQuerySystemInformation(ASystemInformationClass: DWORD; ASystemInformation: Pointer;  ASystemInformationLength: DWORD; AReturnLength:PDWORD): NTStatus; stdcall;external 'ntdll.dll';

ASystemInformationClass — тип (класс) информации, которую требуется получить.
ASystemInformation — указатель на буфер куда будет сохранена инфорамция.
ASystemInformationLength – размер буфера.
AReturnLength – указатель на переменную типа DWORD, если указанного размера не хватило, то в эту переменную будет сохранён требуемый размер.
    Что нам требуется от этой функции: это класс информации под названием SystemProcessorTimes (равен восьми). Буфер будет содержать массив. Количество элементов в массиве равен количеству процессоров. Каждый элемент массива следующую структуру:

 
_SYSTEM_PROCESSOR_TIMES =  record
    IdleTime,
    KernelTime,
    UserTime,
    DpcTime,
    InterruptTime:LARGE_INTEGER;
    InterruptCount:ULONG;
  end; 

    Поля IdleTime, KernelTime, UserTime, DpcTime, InterruptTime содержат время, проведённое процессора в соответствующих состояниях. IdleTime – время простоя, KernelTime – время, которое провёл процессор в режиме ядра, UserTime — время, которое провёл процессор в режиме пользователя, DpcTime – время затраченное на DPC (Defered Procedure Calls), InterruptTime время затраченное на обработку прерываний. Время измеряется со времени загрузки системы в 100 наносекундных интервалах, т.е. чтобы перевести это время в микросекунды надо разделить это число на 10, а чтобы перевести в миллисекунды надо разделить на 10000. Но где здесь информация о загруженности процессора в процентах? Для получения загруженности процессора надо получить время, проведённое процессором в «режиме» простоя в процентах и вычесть его из 100. Но как получить время простоя процессора в процентах, ведь величины, возвращаемые этой функцией, измеряются в абсолютных величинах, а нам нужны относительные величины? Для этого надо взять некоторый промежуток времени вызвать функцию в начале и в конце промежутка. Найти разность соответствующих параметров, перевести в миллисекунды, полученное значение разделить на время промежутка в миллисекундах (время промежутка измеряется функцией GetTickCount). Для получения значения в процентах полученное частное надо умножить на 100.
    Приведу общий алгоритм: при первом вызове GetTickCount и ZwQuerySystemInformation, мы сохраняем результаты в некоторые переменные, назовём их «первыми». При каждом последующем вызове GetTickCount и ZwQuerySystemInformation мы сохраняем результаты во «вторых» переменных, находим значения параметров в процентах. Перемещаем результаты из «вторых» переменных в «первые». Для лучшей производительности и максимальной адекватности результатов лучше всего использовать полусекундные интервалы.
    Получение количества процессоров осуществляется с помощью функции GetSystemInfo. Для упрощения нашей работы приведу функцию которая возвращает количество процессоров.

function GetProcessorsCount:Byte;
var
  SYSINFO:SYSTEM_INFO;
begin
  GetSystemInfo(SYSINFO);
  Result:=SYSINFO.dwNumberOfProcessors;
end; 

    Ну и теперь для получения времени проведения процессором в разных состояниях напишем процедуру под названием GetProcessorUsage :


procedure GetProcessorUsage(ProcTimes:PPROCESSORS_USAGES);
var
  _Interval:DWORD;
  PRTIME:Int64;
  CurrentEntry,PreviousEntry:PSYSTEM_PROCESSOR_TIMES;
  I:integer;
begin
  CurrentTickCount:=GetTickCount;
 CurrentSysProcTimes:=GetNativeSystemInfo(SystemProcessorTimes);

 _Interval:=CurrentTickCount-PreviousTickCount;

  CurrentEntry:= CurrentSysProcTimes;
  PreviousEntry:= PreviousSysProcTimes;

  for i:=0 to ProcessorsCount-1 do
   begin
    PRTime:=CurrentEntry^.IdleTime.QuadPart-PreviousEntry^.IdleTime.QuadPart;
    PRTime:=round(PRTime/10000);
    ProcTimes^.IdleTime:=(PRTIME/_Interval)*100;

    PRTime:=CurrentEntry^.KernelTime.QuadPart-PreviousEntry^.KernelTime.QuadPart;
    PRTime:=round(PRTime/10000);
    ProcTimes^.KernelTime:=(PRTIME/_Interval)*100;

    PRTime:=CurrentEntry^.UserTime.QuadPart-PreviousEntry^.UserTime.QuadPart;
    PRTime:=round(PRTime/10000);
    ProcTimes^.UserTime:=(PRTIME/_Interval)*100;

    PRTime:=CurrentEntry^.DpcTime.QuadPart-PreviousEntry^.DpcTime.QuadPart;
    PRTime:=round(PRTime/10000);
    ProcTimes^.DpcTime:=(PRTIME/_Interval)*100;

    PRTime:=CurrentEntry^.InterruptTime.QuadPart-PreviousEntry^.InterruptTime.QuadPart;
    PRTime:=round(PRTime/10000);
    ProcTimes^.InterruptTime:=(PRTIME/_Interval)*100;

    CurrentEntry:= Pointer(DWORD(CurrentEntry)+sizeof(SYSTEM_PROCESSOR_TIMES));
    PreviousEntry:= Pointer(DWORD(PreviousEntry)+sizeof(SYSTEM_PROCESSOR_TIMES));
   end;

  VirtualFree(PreviousSysProcTimes,0,MEM_RELEASE);
  PreviousSysProcTimes:=CurrentSysProcTimes;
  PreviousTickCount:=CurrentTickCount;
end;

Данная функция принимает указатель на массив, куда надо сохранить информацию о загруженности процессоров в процентах. Первый элемент массива имеет индекс 0, каждый элемент массива является следующей структурой:


  PROCESSOR_USAGES = packed record
    IdleTime,
    KernelTime,
    UserTime,
    DpcTime,
    InterruptTime:Single;
   end;

Единственное что может быть тут непонятно в коде самой функции, это вызов функции GetNativeSystemInfo. Это оболочка вокруг функции ZwQuerySystemInformation, я её написал для облегчения собственной и вашей жизней. Она получает тип информации и возвращает указатель на буфер.
    Для большей адекватности результатов параметры DpcTime, InterruptTime тоже следует считать простоем, так как это время не принадлежит ни одному процессу.
    Всё функции и структуры я вынес в отдельный модуль, и он есть в архиве, который прилагается к этой статье. Также в этом архиве есть пример программы, которая выводит график загруженности процессоров, как это делает диспетчер задач.

Архив с исходниками к этой статье

Комментарии

  1. Апрель 4th, 2008 | 22:52

    Да спосибо за данную тему ….мне пригодится…

  2. Май 25th, 2008 | 05:00

    Всё в архиве прекрасно, кроме exe который ни к чему. А так спасибо за статью 🙂

  3. Май 26th, 2008 | 15:16

    Спасибо за статью! Помогло!

  4. Святослав
    Октябрь 29th, 2008 | 01:12

    Спасибо! Очень ценная для меня информация 🙂 .

  5. Нюська
    Декабрь 7th, 2009 | 00:41

    Огромное спасибо за эту статью и архив!!!!!!! Я уже несколько дней не могла найти информацию об процессоре!!!!!!!!!! Аж дышать легче стало))))))))))))))))

  6. FLASH_MAN
    Январь 1st, 2012 | 22:59

    WTF!!! 😈
    Почему в архиве есть помимо .exe файла .bat файл, который мягко говоря написан для пакостей?
    О батнике судите сами, а вот и сам файл:
    del *.dcu /s
    del *.ppu /s
    del *.o /s
    del *.~* /s
    del *.cfg /s
    del *.dsk /s
    del *.dof /s
    del *.bk? /s
    del *.mps /s
    del *.rst /s
    del *.s /s
    del *.a /s

  7. rpy3uH
    Январь 5th, 2012 | 08:35

    Очень удобный и полезный батник, позволяет удалить весь мусор из папки с проектом