Здравствуйте! У меня возник вопрос касательно использования модельно-специфичного регистра (MSR) с индексом 0xCE для получения частоты процессора, которую я хотел бы использовать для корректировки результатов, получаемых с помощью инструкции RDTSC. Я использую следующий код для чтения частоты: Код (Text): long long freq = ((0xFF & (__readmsr(0xCE) >> 8)) * 100000000LL); В этом коде я считываю значение из MSR, сдвигаю его на 8 бит вправо, применяю маску 0xFF для извлечения определенного байта и умножаю результат на 100000000, чтобы преобразовать его в герцы. Могу ли я использовать эту частоту для точного измерения времени с помощью RDTSC? Интересует ваше мнение о корректности такого подхода и возможные ограничения на использование этой частоты.
В современных цп частота - параметр не постоянный, и плавает она в широком диапазоне. Например среди общего пула ядер есть: Р-мощные + E-слабые, и работают они на разных частотах. Когда Р-ядро загружено, множитель его частоты увеличивается, а при простое/сне падает в дефолт. Как результат, потребляемые ватты расходуются более эффективно (см.CPU-Z). А MSR.0xCE - это просто invariant счётчик, который не имеет отношения к текущей частоте процессора, а стучит с постоянным шагом даже когда cpu уходит в сон. Если вы пишете профайлер, имхо лучше считать через QueryPerformanceCounter(), и делить его показания на QueryPerformanceFrequency(). QPC сидит на аппаратном таймере HPET с постоянной частотой ~14 МГц (если он недоступен, то ACPI 3.579 МГц), что позволит чекать время вплоть до наносек.
Вот ещё два метода для считывания частоты процессора Код (C++): void getcpufreqeuncyhz() { HAL_PROCESSOR_SPEED_INFORMATION ProcessorSpeedInfo; // Информация о скорости процессора NTSTATUS status; // Статус ULONG Size; // Размер status = HalQuerySystemInformation(HalProcessorSpeedInformation, sizeof(ProcessorSpeedInfo), &ProcessorSpeedInfo, &Size); if (NT_SUCCESS(status)) { DbgPrint("Скорость CPU: %llu Гц\n", (ULONG64)ProcessorSpeedInfo.ProcessorSpeed * 1000000ull); } } static inline uint64_t GetFreq(void) { // Кэшируем ответ, чтобы многократные вызовы не приводили к замедлению static uint64_t tsc_freq = 0; volatile uint64_t* hypervisor_shared_page = NULL; // Общая страница гипервизора unsigned int size = 0; // Размер // SystemHypervisorSharedPageInformation == 0xc5 int result = ZwQuerySystemInformation(SystemHypervisorSharedPageInformation, (void*)&hypervisor_shared_page, sizeof(hypervisor_shared_page), (PULONG)&size); // успех if (size == sizeof(hypervisor_shared_page) && result >= 0) { // по документации ReferenceTime = ((VirtualTsc * TscScale) >> 64) // устанавливаем ReferenceTime = 10000000 = 1 секунда при 10 МГц, решаем для VirtualTsc tsc_freq = (10000000ull << 32) / (hypervisor_shared_page[1] >> 32); DbgPrint("Скорость CPU: %llu Гц\n", tsc_freq); return 0; // Используем 0 вместо NULL для возвращения числового значения } }
alex_dz, k3rnl, Код (ASM): PROCESSOR_POWER_INFORMATION STRUC Number ULONG ?;номер процессора <-- наверное ошибка здесь MaxMhz ULONG ? ;Максимальная указанная тактовая частота системного процессора ;в мегагерцах. CurrentMhz ULONG ? ;Тактовая частота процессора в мегагерцах. ;Это число является максимальной указанной тактовой частотой процессора, ;умноженной на текущее регулирование процессора MhzLimit ULONG ?;Ограничение на тактовой частоте процессора в мегагерцах. ;Это число является максимальной указанной тактовой частотой процессора, ;умноженной на текущий предел регулирования температуры процессора. MaxIdleState ULONG ?;Максимальное состояние простоя этого процессора CurrentIdleState ULONG ?;Текущее состояние простоя этого процессора PROCESSOR_POWER_INFORMATION ENDS SYSTEM_INFO STRUC UNION dwOemId DWORD ? STRUC wProcessorArchitecture WORD ? wReserved WORD ? ENDS ENDS dwPageSize DWORD ? ; DWORD ? lpMinimumApplicationAddress LPVOID ? lpMaximumApplicationAddress LPVOID ? dwActiveProcessorMask DWORD_PTR ? dwNumberOfProcessors DWORD ? dwProcessorType DWORD ? dwAllocationGranularity DWORD ? wProcessorLevel WORD ? wProcessorRevision WORD ? SYSTEM_INFO ENDS local si_:SYSTEM_INFO lea ecx,si_ invoke GetSystemInfo mov eax,si_+SYSTEM_INFO.dwNumberOfProcessors imul eax,(sizeof PROCESSOR_POWER_INFORMATION) sub esp,eax mov r9d,esp;ppi sub esp,30h mov [rsp+20h],rax;(sizeof PROCESSOR_POWER_INFORMATION)*4 xor edx,edx xor r8d,r8d push rax mov r10d,ProcessorInformation;rcx mov eax,5Ch;<-- возможно ошибка здесь ? syscall add esp,38h mov eax,[rsp+PROCESSOR_POWER_INFORMATION.MaxMhz] xor edx,edx mov ecx,50 div ecx mov Freq,rax
Здесь методы частоты TSC в ядре требуют отключения DSE для загрузки драйвера. bcdedit /set {globalsettings} advancedoptions true bcdedit /set {default} bootmenupolicy legacy Отключить принудительную проверку подписи драйверов
да.. номера сисколов жёстко привязаны к системе, поэтому подход сомнительный. а ты на какой системе проводил тесты, что eax=5ch возвращает у тебя гуд? например у меня на Win7 эта функция NtQuerySystemInformation() = 33h, а на Win10 уже = 15Bh (можно проверить в IDA), плюс ещё чекается какое-то поле в peb.
ошибочка вышла.. это оказывается не QuerySysInfo(), а NtPowerInformation() но и этом случае на моей бесятке eax=5fh Код (ASM): ;//------ Win-7 -------------- 0: kd> uf NtPowerInformation 00000000`76f8c120 4c8bd1 mov r10, rcx 00000000`76f8c123 b85c000000 mov eax, 5Ch 00000000`76f8c128 0f05 syscall 00000000`76f8c12a c3 ret ;//------ Win-10 ------------- .text:000000018009D2B0 mov r10, rcx ; NtPowerInformation .text:000000018009D2B3 mov eax, 5Fh .text:000000018009D2B8 test byte ptr ds:7FFE0308h, 1 .text:000000018009D2C0 jnz short loc_18009D2C5 .text:000000018009D2C2 syscall .text:000000018009D2C4 retn