Получение информации о загрузке системы.
Доброго времени суток. В этой небольшой статье я расскажу, как можно получить информацию о загрузке каждого процессора системы. Конечно, есть уже готовые библиотеки для получения загрузки системы, но прочитав эту статью, вы узнаете, как эти библиотеки узнают загруженность процессоров в процентах, ведь ни одна из стандартных 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 тоже следует считать простоем, так как это время не принадлежит ни одному процессу.
Всё функции и структуры я вынес в отдельный модуль, и он есть в архиве, который прилагается к этой статье. Также в этом архиве есть пример программы, которая выводит график загруженности процессоров, как это делает диспетчер задач.
Да спосибо за данную тему ….мне пригодится…
Всё в архиве прекрасно, кроме exe который ни к чему. А так спасибо за статью 🙂
Спасибо за статью! Помогло!
Спасибо! Очень ценная для меня информация 🙂 .
Огромное спасибо за эту статью и архив!!!!!!! Я уже несколько дней не могла найти информацию об процессоре!!!!!!!!!! Аж дышать легче стало))))))))))))))))
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
Очень удобный и полезный батник, позволяет удалить весь мусор из папки с проектом