Сразу хочу предупредить правообладателей, что этот пост касается только исходного кода Windows 2000, который доступен в интернете и которого нет на моей машине. Единственная цель с которой я это все делаю – это лучше знакомство и понимание читателя, что такое BSOD. Этот материал предназначен в основном для ИТ специалистов, мы будем смотреть исходный код Windows 2000 :).
Итак приступим. Начнем с кода, который непосредственно вызывает сам крах, делает дампы и т.д. Это функция с именем KeBugCheckEx, вот ее полная сигнатура
VOID KeBugCheckEx ( IN ULONG BugCheckCode, IN ULONG_PTR BugCheckParameter1, IN ULONG_PTR BugCheckParameter2, IN ULONG_PTR BugCheckParameter3, IN ULONG_PTR BugCheckParameter4 )
Обратите внимание на комментарий:
Routine Description: This function crashes the system in a controlled manner.
Я опускаю часть кода не до конца понятную для меня, например:
// // Try to simulate a power failure for Cluster testing // if (BugCheckCode == POWER_FAILURE_SIMULATE) { KiScanBugCheckCallbackList(); HalReturnToFirmware(HalRebootRoutine); }
Здесь можно предположить, что если стоп код POWER_FAILURE_SIMULATE то мы должны проверить установленные функции обратных вызовов (которые что-то сделают) и вызвать какую-то процедуру специфичную для кластеров, под который сделан собран специфичный HAL, опять же повторюсь для нас это не важно с точки зрения понимания работы самого механизма, а любознательный читатель может сам покопаться более глубоко.
Далее выполняются вызовы:
RtlCaptureContext(&KeGetCurrentPrcb()->ProcessorState.ContextFrame); KiSaveProcessorControlState(&KeGetCurrentPrcb()->ProcessorState);
Опять не будем уделять внимание детализации работы этих функций, можно предположить, что их смысл в сохранении данных которые касаются текущего состояния процессора, например, значения регистров. Только обратим внимание, что при анализе дампов в отладчике Windbg есть специальное расширение !prcb, которое отображает информацию о контрольном блоке процессора. Вот пример:
Далее опять же будет код, который будет выполнять дополнительные проверки, опускаем его разбор и доходим до кода, который показывает синий экран смерти:
if (InbvIsBootDriverInstalled()) { InbvAcquireDisplayOwnership(); InbvResetDisplay(); InbvSolidColorFill(0,0,639,479,4); // make the screen blue InbvSetTextColor(15); InbvInstallDisplayStringFilter((INBV_DISPLAY_STRING_FILTER)NULL); InbvEnableDisplayString(TRUE); // enable display string InbvSetScrollRegion(0,0,639,479); // set to use entire screen } if (!hardErrorCalled) { sprintf((char *)Buffer, " *** STOP: 0x%08lX (0x%p,0x%p,0x%p,0x%p) ", BugCheckCode, BugCheckParameter1, BugCheckParameter2, BugCheckParameter3, BugCheckParameter4 ); InbvDisplayString((char *)Buffer); KeGetBugMessageText(BugCheckCode, NULL); InbvDisplayString(" "); if (KiBugCheckDriver != NULL) { // // Output the driver name. // KeGetBugMessageText(BUGCODE_ID_DRIVER, NULL); InbvDisplayString(AnsiBuffer); InbvDisplayString(" "); }
Теперь нам нужно записать дамп памяти, делаем и это:
KeGetCurrentPrcb()->ProcessorState.ContextFrame = ContextSave; if (!IoWriteCrashDump(BugCheckCode, BugCheckParameter1, BugCheckParameter2, BugCheckParameter3, BugCheckParameter4, &ContextSave )) {
В интернете можно найти описание ф-ции IoWriteCrashDump, мы не будем ее разбирать, обращаю только внимание, что TRIAGE_DUMP – этот минидамп. Минидамп не включает в себя содержимое всех страниц памяти.
Я этим постом хотел лишь показать, что разработчикам ОС Windows нужно ставить лайки за то, что они предусмотрели механизм BSOD, который:
- предотвращает более серьезные повреждение данных;
- предоставляет возможность анализировать дампы памяти и находить проблему.
Этот обзор очень краток и не был бы полным без некоторых примером. Рассмотрим как генерируется крах INACCESSIBLE_BOOT_DEVICE
Есть такая функция, вот ее сигнатура:
NTSTATUS IopMountVolume( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN AllowRawMount, IN BOOLEAN DeviceLockAlreadyHeld, IN BOOLEAN Alertable )
Эта функция выполняет монтирование тома на указанном устройстве. Если в ходе операции возникли проблемы выполняется следующий участок кода:
// // Finally, if the mount operation failed, and the target device is the // boot partition, then bugcheck the system. It is not possible for the // system to run properly if the system's boot partition cannot be mounted. // // Note: Don't bugcheck if the system is already booted. // if (!NT_SUCCESS( status ) && DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION && InitializationPhase < 2) { KeBugCheckEx( INACCESSIBLE_BOOT_DEVICE, (ULONG_PTR) DeviceObject, status, 0, 0 ); }
Смысл этого кода показать читатель, что синий экран смерти (BSOD), это нечто сродни ошибки в приложении, например, если вы забили вставить дискетку, а ваша программа пытается считать файл оттуда и выдает ошибку “Ошибка чтения файла с диска A:”