Что делать, если в QEMU всё работает нормально, а на реальном компьютере с процессором Celeron D система зависает или выходит из строя?

С марта разрабатываю операционную систему на C/C++ и ассемблере под x86. Прошел классический путь: от вывода первой строки в текстовом режиме до самописного GUI с «грязными» прямоугольниками, двойной буферизацией, кастомными шрифтами и тасками в сине-голубой гамме. Раньше тесты запускал исключительно на реальном железе — Intel Celeron D 326 с интегрированной графикой. Чтобы не гонять каждый раз флешку и не использовать dd, в версии 3.17.9 вернул поддержку QEMU. Попытка «усидеть на двух стульях» привела к багу, который не могу исправить уже несколько дней.
  • 1 июля 2026 г. 20:01
Ответы на вопрос 2
Коротко: QEMU сильно мягчит ошибки (и по‑умолчанию отличается по CPU, графике, MTRR/PCI и поведению BIOS). Чтобы понять, почему ваш код валится именно на Celeron D, нужно последовательно сузить область ответственности и добавить надёжные средства ранней отладки. Ниже — практичный чек‑лист с приоритетными шагами и конкретными приёмами.

1) Организуйте надёжный вывод ранней отладки (serial)
- Сделайте вывод всех ранних сообщений на COM1 (UART 16550) — это самый надёжный способ поймать крах до графики/видео.  
- Подключите USB–TTL адаптер к реальной машине и снимайте лог в терминале (minicom, screen).  
- Пример: инициализация 0x3F8, baud 115200, запись байта через outb(0x3F8, c).

2) Минимизируйте и бинарно/поэтапно локализуйте проблему
- Соберите «минимальный» образ, который только инициализирует память, выключает прерывания и печатает строку. Если он работает — добавляйте блоки кода по одному (память, графика, multitasking, драйверы), чтобы увидеть, при добавлении чего ломается.  
- Это быстрый способ обнаружить, какой функционал вызывает расхождение QEMU ↔ реальная плата.

3) Воспроизведите поведение QEMU как можно точнее
- Запускайте QEMU с тем же типом CPU: qemu-system-i386 -cpu pentium4 (или core2, pentium3 — подберите ближе к Celeron D). QEMU по умолчанию может эмулировать более современный CPU.  
- Используйте -serial stdio и -monitor stdio, -S -s для отладки через gdb.  
- Сравните CPUID, MSR и состояние регистров в QEMU и на реальном устройстве (выведите cpuid информация при загрузке).

4) Проверки, которые почти всегда спасают
- A20, GDT/IDT, CR0/CR4/CR3: убедитесь, что вы корректно включаете/выключаете нужные биты (WP, NE, PG и т.д.). QEMU может маскировать мелкие ошибки.  
- Инициализируйте FPU/SSE: fninit/fnstcw/сброс CR0.EM и CR4.OSFXSR? BIOS может по‑разному оставлять состояние FPU/SSE — некоторые операции с SSE приведут к падению, если не инициализировать.  
- Выключите прерывания до полной установки IDT/TSS. На реальном железе таймер и прочие прерывания могут прибыть раньше, чем вы готовы.  
- Стек/выравнивание стекa: для SSE инструкции и для некоторых ABI нужен 16‑байтный выравненный стек при вызове функций (movaps/lddqu и т.п.) — QEMU иногда не проявляет проблему, а реальный CPU — да. Добавьте выравнивание или компиляторские опции/атрибуты.  
- Инициализируйте/проверьте TSS и переключение стека при прерываниях, если вы используете отдельные стеки.

5) Графика / VESA / MMIO / MTRR
- QEMU VGA / VBE гораздо проще. Интегрированная Intel‑графика может предъявлять особые требования: нужно правильно получить линейный framebuffer через VESA или через PCI BAR MMIO и учесть кеширование (MTRR или PAT/WC).  
- Если вы рисуете в LFB — убедитесь, что адрес действительный и что установлен правильный тип памяти (write‑combining), либо используйте простой VGA текст/planar режим для отладки графики.  
- Проблемы с MTRR/WC часто проявляются как «коррупция» или зависания при интенсивной записи в фрейм‑буфер.

6) APIC/IRQ и мультипроцессор
- Проверьте, как настроен APIC: QEMU может предоставлять APIC иначе, чем на плате. Если у вас код работает с LAPIC, убедитесь, что правильно установлены MSR IA32_APIC_BASE и что вы корректно обрабатываете IRQ.  
- Если у вас однопоточный код, временно выключите/не инициализируйте APIC и переключитесь на PIC, чтобы проверить влияние.

7) Сборка и оптимизации
- Скомпилируйте версию с -O0 и без агрессивных оптимизаций (-fno-omit-frame-pointer) и попробуйте. Часто баги проявляются только при оптимизациях (выравнивание, реорганизация стека).  
- Попробуйте собрать без использования SSE (-mno-sse/-mno-sse2) или явно выровнять критические структуры ( __attribute__((aligned(16))) ).  
- Добавьте «canaries»/проверки переполнения стека/heap, заполнение освобождённой памяти 0xAA/0x55, чтобы поймать коррумпцию.

8) Ловите исключения и регистры
- Установите обработчики для всех исключений (DF, GP, PF, UD, etc.), которые печатают регистры (EIP, CS, EFLAGS, CR2 и т.д.) на serial перед halt. Это даст точку входа для диагностики.  
- Если у вас падает в графическом режиме, переведите обработчик исключений на COM1 ещё до настройки видеорежима.

9) Используйте внешние средства отладки
- Подключите аппаратный USB‑UART, используйте внешнюю машину с gdb + kgdb по serial если возможно.  
- Попробуйте протестировать образ на других реальных машинах (старые Pentium/Atom/ноутбуки), чтобы понять, специфично ли это именно Celeron D.

10) Логика плохих мест, на которые обратить внимание в первую очередь
- Неправильная работа с видеопамятью (неправильный LFB, MTRR, кеширование).  
- SSE/выравнивание стека или FPU не инициализирован.  
- Прерывания приходят раньше готовности IDT/TSS.  
- Неправильная обработка PCI/BIOS данных (BAR, MMIO), адреса за пределами доступной RAM.  
- Использование инструкции, которую CPU не поддерживает (или поддерживает иначе) — проверьте CPUID и MSR.

11) Практические команды / примеры
- QEMU: qemu-system-i386 -cpu pentium4 -m 512M -serial stdio -S -s  
  (запустите gdb на порт 1234: target remote :1234)  
- Serial capture: screen /dev/ttyUSB0 115200

Если хотите — пришлите:
- логи serial с реальной машины (до и после падения),  
- вывод CPUID (в виде hex),  
- маленький минимальный образ/код, который стабильно ломается на Celeron D, но работает в QEMU.  

Могу помочь проанализировать конкретные логи/дампы и подсказать, где искать дальше.
Я проанализировал твой проект, что могу сказать: <br/> <br/> Симптомы 100% указывают на кривую настройку таймера (PIT / APIC), конфликт прерываний и закомментированный `hlt`. <br/> <br/> Разбор симптомов: <br/> <br/> 1. Ускорение спикера и замедление системы <br/> Ты настраиваешь PIT (или APIC timer) под определенную частоту, но, скорее всего, рассчитываешь делитель неправильно. Базовая частота PIT — строго 1193182 Гц. Если твой математический расчет делителя в коде глючит (или ты берешь другую базовую частоту), прерывания таймера (IRQ 0) срабатывают в 2 раза чаще. <br/> В итоге планировщик (scheduler) получает тики в 2 раза чаще, система захлебывается в обработчиках прерываний (ISR) и переключениях контекста. Отсюда дикая просадка производительности даже в покое. <br/> PC Speaker использует канал 2 того же PIT. Если базовая частота или делитель завышены в 2 раза, он пищит ровно в 2 раза быстрее (2 секунды превращаются в 1). <br/> <br/> 2. Влияние мыши на таймер <br/> PS/2 мышь использует IRQ 12, таймер использует IRQ 0. И то, и другое идет через контроллер прерываний (PIC). <br/> Если инициализация мыши меняет маску PIC или переназначает векторы, она ломает обработку IRQ 0. Ты пишешь, что закомментировал mouse_init, и звук стал пролетать еще быстрее. Значит, мышь как-то маскировала часть прерываний таймера (или генерировала спурайные прерывания), а без нее таймер срабатывает еще чаще. <br/> <br/> 3. Курсор и время не отрисовываются <br/> Отрисовка времени и курсора у тебя, очевидно, завязана на тики ядра. Раз таймер работает криво, тики не доходят до нужного обработчика, либо ты зависаешь в ISR, и графическая подсистема не получает квант времени на перерисовку (dirty rects). <br/> <br/> Что чинить: <br/> 1. Проверь расчет делителя для PIT (порт 0x40). Делитель = 1193182 / желаемая_частота. <br/> 2. Проверь инициализацию PIC (порта 0x20 и 0xA0). Убедись, что маскирование IRQ 0 (таймер) и IRQ 12 (мышь) сделано правильно. <br/> 3. Отключи APIC (если включал), работай только через старый 8259 PIC, пока не починишь базовый таймер. <br/> 4. Проверь, не генерирует ли мышь спурайные прерывания (spurious interrupts), которые блокируют очередь PIC.
Похожие вопросы