Я отслеживаю некоторые машины с помощью WMI, используя System.Management
от .NET. Я использую следующий запрос:
SELECT Timestamp_Sys100NS, PercentProcessorTime
FROM Win32_PerfRawData_PerfOS_Processor
WHERE Name='_Total'
Из него я вычисляю % использования процессора, используя хорошо известную формулу:
double cpu_usage = (1 - (double)delta_cpu / delta_time) * 100;
Это работает очень хорошо на всех машинах, кроме одной (пока).
Проблема в том, что для одной машины, которая является Windows 2003 server (с включенной гиперпоточностью, если это имеет значение), я иногда получаю отрицательные значения использования CPU. Другими словами, выражение (double)delta_cpu / delta_time
дает число > 1
. Я искал в Интернете подсказки, почему это может происходить, но ничего не нашел.
Является ли это спецификой Windows 2003 server? Или это проблема, связанная с гиперпоточностью? Или это просто ожидаемо, и я должен просто зажать значение использования процессора или cpu_delta
в какой-то диапазон?
EDIT:
Вторая странность, которую я наблюдаю на этой машине, заключается в том, что значение Timestamp_Sys100NS
не показывает FILETIME
как дату (тики с эпохи 1 января 1600 года), а вместо этого выглядит как тики с момента загрузки.
EDIT 2: Теперь я убедился, что эта проблема наблюдается на многих серверах Windows 2003. И, видимо, я не единственный, у кого такая же проблема.
ПРАВКА 3:
Я решил проблему с меткой времени, запросив LastBootUpTime
из Win32_OperatingSystem
и добавив его к Timestamp_Sys100NS
, когда значение Timestamp_Sys100NS
слишком далеко в прошлом. Это, похоже, дает правильные дату и время. Код, управляющий датой после ее получения из Win32_OperatingSystem
, выглядит следующим образом:
WbemScripting.SWbemDateTime swbem_time = new WbemScripting.SWbemDateTime();
swbem_time.Value = date_str;
string time_as_file_time_str = swbem_time.GetFileTime(true);
return new DateTimeOffset(epoch.Ticks + long.Parse(time_as_file_time_str),
swbem_time.UTCSpecified
? TimeSpan.FromMinutes(swbem_time.UTC)
: TimeSpan.Zero);
...затем корректировка до UTC...
boot_time = boot_time.UtcDateTime;
...затем boot_time
просто добавляется к метке времени (current
), возвращаемой WMI в поле Timestamp_Sys100NS
...
if (time.Year < 2000)
time = boot_time + current;
EDIT 4:
Похоже, что существует 3 класса систем относительно Timestamp_Sys100NS
:
Timestamp_Sys100NS
- это время в тиках от эпохи в UTC. Timestamp_Sys100NS
нужно добавить к Win32_OperatingSystem.LastBootUpTime
, чтобы получить разумное время. EDIT 5: Некоторые из пострадавших машин могли быть виртуальными машинами, но не все.