OSDev: Почему моя функция выделения памяти внезапно перестает работать в функции инициализации AHCI? - PullRequest
0 голосов
/ 14 апреля 2019

После того, как мое ядро ​​вызывает функцию AHCIInit () внутри функции ArchInit (), я получаю ошибку страницы в одном из вызовов MemAllocate (), и это происходит только на реальных машинах, так как я пытался реплицировать ее на VirtualBox,VMWare и QEMU.

Я попытался отладить код, провести модульное тестирование распределителя памяти и удалить все из ядра, за исключением диспетчера памяти и самого драйвера AHCI, единственное, что я обнаружил, - это что-топовреждает блоки выделения, вызывая ошибку страницы MemAllocate ().

Весь исходный код ядра находится в https://github.com/CHOSTeam/CHicago-Kernel,, но основные файлы, в которых, вероятно, возникает проблема:
https://github.com/CHOSTeam/CHicago-Kernel/blob/master/mm/alloc.c
https://github.com/CHOSTeam/CHicago-Kernel/blob/master/arch/x86/io/ahci.c

Я ожидал, что AHCIInit () обнаружит и инициализирует все устройства AHCI, и загрузка продолжится, пока не достигнет диспетчера сеансов или оболочки ядра, но на реальных компьютерах страница сбоится до того, как дажеинициализация планировщика (так что нет, проблема не в моем планировщике).

1 Ответ

0 голосов
/ 14 апреля 2019

Если это работает в эмуляторах, но не работает на реальном оборудовании;тогда первое, что я подозреваю, это:

  • ошибки в управлении физической памятью.Например, инициализация диспетчера физической памяти не округляет «начальный адрес используемой области ОЗУ» до границы страницы или не округляет «конечный адрес используемой области ОЗУ» до границы страницы, в результате чего получается «половина используемой ОЗУ и половина неиспользуемой ОЗУ»."страница, которая будет выделена кучей позже (где она работает на эмуляторах, потому что карта памяти, предоставляемая прошивкой, в любом случае описывает области, которые хорошо выровнены).

  • ошибка, где ОЗУпредполагается, что он содержит нули, но может и не быть (там, где он работает на эмуляторах, поскольку они, как правило, оставляют почти всю оперативную память заполненной нулями).

  • состояние гонки (когда разные временные характеристики вызывают разное поведение).

Однако;это монолитное ядро, что означает, что вы будете постоянно сталкиваться с тем, что «любой кусок кода в пространстве ядра делал что-то, что вызывало проблемы для любого другого фрагмента кода где-либо еще»;и есть куча распространенных ошибок с использованием памяти (например, случайная запись после конца того, что вы выделили).По этой причине я хотел бы иметь более эффективные инструменты для диагностики проблем, особенно для кучи.

В частности, для кучи, которую я бы начал с канареек (например, ставьте магическое число, например 0xFEEDFACE, перед каждым блоком памяти вкуча и другое другое число после каждого блока памяти в куче, а затем проверьте, что магические числа все еще присутствуют и исправляют, где это удобно - например, когда блоки освобождаются или изменяются в размере.Затем я написал бы функцию «check_heap ()», которая сканирует все, насколько это возможно, проверяя (канарейки, если статистика, такая как «количество свободных блоков», действительно правильная и т. Д.).Идея заключается в том, что (когда вы подозреваете, что что-то могло повредить кучу), вы можете вставить вызов функции «check_heap ()» и перемещать этот вызов, пока не выясните, какой фрагмент кода вызвал повреждение кучи.Я бы также предложил иметь параметр «что» в вашем «kmalloc() или эквивалентном» (например, чтобы вы могли делать такие вещи, как myFooStructure = kmalloc("Foo Structure", sizeof(struct foo));), где предоставленная «какая строка» хранится в метаданных выделенного блока,чтобы потом (когда вы обнаружите, что куча была повреждена), вы можете отобразить «какую строку», связанную с блоком до повреждения, и чтобы вы могли (например) перечислить, сколько из каждого типа вещей в настоящее время существуетпомогите определить, что является утечкой памяти (например, если количество блоков "Foo Structure" постоянно увеличивается).Конечно, эти вещи могут быть (должны быть?) Включены / отключены с помощью параметров времени компиляции (например, #ifdef DEBUG_HEAP).

Другая вещь, которую я бы порекомендовал, это самопроверка.Это как модульные тесты, но они встроены непосредственно в ядро ​​и присутствуют всегда.Например, вы могли бы написать код, чтобы вытолкнуть дневные лучи из кучи (например, выделить части памяти произвольного размера и заполнить их чем-нибудь, пока не закончится память, затем освободить половину из них, а затем выделить больше, пока не закончится памятьснова и т. д .; при вызове функции check_heap () между каждым шагом);где этот код может / должен принимать параметр «сколько ударов» (чтобы вы могли потратить небольшое количество времени на самотестирование или огромное количество времени на самотестирование).Вы также можете написать код, чтобы вытолкнуть дневной свет из менеджера виртуальной памяти и менеджера физической памяти (и планировщика, и ...).Тогда вы могли бы решить всегда выполнять небольшое количество самотестирования каждый раз при загрузке ядра и / или предоставлять специальный параметр / опцию ядра для включения «чрезвычайно тщательного режима самопроверки».

Не забывайте, что в конечном итоге (если / когда будет выпущена ОС) вам, вероятно, придется прибегнуть к «удаленной отладке по электронной почте» (например, когда кто-то без опыта программирования, кто может не очень хорошо знать английский, отправляет вам электронное письмо с сообщением «ОС не работает», и вы должны попытаться выяснить, что идет не так, прежде чем счетчик «хлопот» конечного пользователя, прежде чем сдаться и больше не заботиться »).

...