Роберт Лав - Разработка ядра Linux
В файле Documentation/sysrq.txt, который находится в каталоге исходных кодов ядра, приводится более полное описание. Реализация поддержки магической комбинации клавиш находится в файле drivers/char/sysrq.с. Магические комбинации клавиш SysRq — жизненно необходимый инструмент, который помогает в отладке и сохранении "гибнущей" системы, так как предоставляет большие возможности для любого пользователя при работе с консолью. Тем не менее необходимо соблюдать осторожность при его использовании на критичных машинах. Если же машина используется для разработок, то польза от этих команд огромная.
Сага об отладчике ядра
Многие разработчики ядра давно высказываются о необходимости встроенного в ядро отладчика. К сожалению, Линус не желает видеть отладчик ядра в своем дереве исходного кода, Он уверен, что использование программ-отладчиков приводит к плохому исправлению ошибок неправильно информированными разработчиками. Никто не может поспорить с его логикой — исправления ошибок, построенные на основании хорошего понимания кода скорее всего будут верными. Тем не менее большинство разработчиков ядра все же нуждаются в официальном отладчике, встроенном в ядро. Поскольку такая возможность навряд ли появится в ближайшее время, то взамен было разработано несколько заплат, которые добавляют поддержку отладчика в стандартном ядре. Не смотря на то, что это внешние и неофициальные заплаты, они являются мощными инструментами с высокой функциональностью. Перед тем, как обращаться к этим решениям, посмотрим, на сколько нам может помочь стандартный отладчик ОС Linux — gdb.
Использование отладчика gdb
Для того, чтобы мельком заглянуть внутрь работающего ядра можно использовать стандартный отладчик GNU. Запуск отладчика для работы с ядром почти ни чем не отличается от отладки выполняющегося процесса.
gdb vmlinux /proc/kcore
Файл vmlinux — это декомпрессированный исполняемый образ ядра, который хранится в корне каталога исходных кодов, где выполнялась сборка выполняющегося ядра. Сжатые файлы zImage, или bzImage использовать нельзя.
Опциональный параметр /proc/kcore исполняет роль файла core, чтобы позволить отладчику читать из памяти выполняющегося ядра. Чтобы иметь возможность читать этот файл, необходимо иметь права пользователя root.
Можно пользоваться практически всеми командами программы gdb для чтения информации. Например, чтобы напечатать значение переменной можно воспользоваться командой.
p global_variable
Для того, чтобы дизассемблировать код функции можно выполнить следующую команду.
disassemble function
Если ядро было скомпилировано с указанием флага -g (необходимо добавить -g к значению переменной CFLAGS в файле Makefile ядра), то отладчик gdb сможет выдавать больше информации. Например, можно выводить дампы структур данных и разыменовывать указатели. При этом также получается ядро значительно большего размера, поэтому для обычной работы не следует компилировать ядро с отладочной информацией.
К сожалению, на этом заканчиваются возможности использования отладчика gdb. С его помощью никак нельзя изменять данные ядра. Нет возможности пошагово выполнять код ядра, или устанавливать точки остановки (breakpoint). Невозможность изменять структуры данных ядра — это большой недостаток. Хотя очень полезно иметь возможность дизассемблировать код функций, еще более полезной была бы возможность изменять структуры данных.
Отладчик kgdb
Отладчик kgdb — это заплата ядра, которая позволяет с помощью отладчика gdb отлаживать ядро по линии последовательной передачи. Для этого требуется два компьютера. На первом выполняется ядро с заплатой kgdb. Второй компьютер используется для отладки ядра по линии последовательной передачи (нуль-модемный кабель, соединяющий две машины) с помощью gdb. Благодаря отладчику kgdb полностью доступен весь набор функций gdb: чтение и запись любых переменных, установка точек остановки, установка точек слежения (watch points), пошаговое исполнение и др.. Специальные версии kgdb даже позволяют вызывать функции.
Установка kgdb и линии последовательной передачи несколько сложная процедура, но если ее выполнить, то отладка ядра значительно упрощается. Заплата ядра также устанавливает большое количество документации в каталог Documentation/, ее следует прочитать.
Несколько человек выполняют поддержку заплаты kgdb для различных аппаратных платформ и версий ядра. Поиск в Интернет — наилучший способ найти необходимую заплату для заданного ядра.
Отладчик kdb
Альтернативой kgdb является отладчик kdb. В отличие от kgdb отладчик kdb — не удаленный отладчик. Отладчик kdb — это заплата, которая сильно модифицирует ядро и позволяет выполнять отладку прямо на той же машине, где выполняется ядро. Кроме всего прочего поддерживается возможность изменения переменных, установки точек остановки и пошаговое выполнение. Выполнять отладку просто — необходимо нажать на консоли клавишу break. При выводе сообщения oops переход в отладчик выполняется автоматически. Более подробная документация доступна в каталоге Documentation/kdb после применения заплаты. Заплата kdb доступна в Интернет по адресу http://oss.sgi.com/.
Исследование и тестирование системы
По мере того, как вы будете накапливать опыт в отладке ядра, у вас будет появляться все больше маленьких хитростей, которые помогают в исследовании и тестировании ядра для получения ответов на интересующие вопросы. Так как отладка ядра требует больших усилий, то каждый маленький совет, или хитрость может оказаться полезным. Рассмотрим несколько таких хитростей.
Использование идентификатора UID в качестве условия
Если разрабатываемый код связан с контекстом процесса, то иногда появляется возможность выполнить альтернативную реализацию не "ломая" существующий код. Это важно, если необходимо переписать важный системный вызов и при этом необходима полностью функционирующая система, на которой этот вызов нужно отладить. Например, допустим, что нужно переписать алгоритм работы системного вызова fork(), который бы использовал некоторые новые возможности, которые уже существуют в ядре. Если сразу не получится все сделать так как надо, то будет очень тяжело отлаживать ядро, так как неработающий системный вызов fork() скорее всего приведет к неработоспособности системы. Но как и всегда, есть надежда.
Часто безопасным будет сохранить старый алгоритм, а новую реализацию выполнить в другом месте. Этого можно достичь используя идентификатор пользователя (UID) в качестве условия того, какой алгоритм использовать.
if (current->uid != 7777) {
/* старый алгоритм ... */
} else {
/* новый алгоритм ... */
}
Все пользователи, кроме того, у которого идентификатор UID равен 7777 будут использовать старый алгоритм. Для тестирования нового алгоритма можно создать нового пользователя с идентификатором 7777. Это позволяет более просто оттестировать критические участки кода, связанные с выполнением процессов.
Использование условных переменных
Если код, который необходимо протестировать, выполняется не в контексте процесса, или необходим более глобальный метод для контроля новых функций, то можно использовать условные переменные. Этот подход даже более простой, чем использование идентификатора пользователя. Необходимо просто создать глобальную переменную и использовать ее в качестве условия выполнения того, или другого участка кода. Если значение переменной равно нулю, то следует выполнить один участок кода. Если переменная не равна нулю, то выполняется другой участок. Значение переменной может быть установлено с помощью отладчика, или специального экспортируемого интерфейса.
Использование статистики
Иногда необходимо получить представление о том, насколько часто происходит некоторое событие. Иногда требуется сравнить несколько событий и вычислить характеристики для их сравнения. Это очень легко сделать путем введения статистки и механизма для экспортирования соответствующих параметров.
Например, допустим, что необходимо выяснить на сколько часто происходит событие foo и событие bar. В файле исходного кода, в идеале там, где соответствующие события возникают, вводится две глобальные переменные.
unsigned long foo_stat = 0;
unsigned long bar_stat = 0;
Как только наступает интересующее событие, значение соответствующей переменной увеличивается на единицу. Эти переменные могут быть экспортированы как угодно. Например, можно создать интерфейс к ним через файловую систему /proc, или написать свой системный вызов. Наиболее просто прочитать их значение с помощью отладчика.