Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 4.5. Система отладки неисправностей
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
4.5. Система отладки неисправностей hardorsoft 05.12.10 в 08:41 |
|
|
Даже если вы использовали все техники мониторинга и отладки, иногда в драйвере остаются ошибки и система выдаёт ошибки при работе драйвера. Когда это происходит, важно уметь собрать столько информации, сколько возможно, чтобы решить эту проблему. hardorsoft 05.12.10 в 08:41 |
||
|
Отметим, что "ошибка", не означает "паника". Код Linux является достаточно прочным, чтобы корректно отреагировать на большинство ошибок: ошибки обычно приводят к уничтожению текущего процесса, в то время как система продолжает работать. Система может паниковать, и это может быть, если ошибка происходит за пределами контекста процесса или если скомпрометированы некоторые жизненно важные части системы. Но когда проблема вызвана ошибкой драйвера, это обычно приводит только к внезапной смерти процесса, которому не повезло использовать драйвер. Только неустранимые повреждения при разрушении процесса приводят к тому, что некоторый объём памяти, выделенной контексту процесса, теряется; например, могли бы быть утеряны динамические списки, выделенные драйвером через kmalloc. Однако, поскольку ядро вызывает операцию close для любого открытого устройства, когда процесс умирает, драйвер может освободить то, что выделено методом open. hardorsoft 05.12.10 в 08:42 |
||
|
Хотя даже Oops (ой) обычно не доводит до падения всей системы, вы можете посчитать, что необходима перезагрузка после того, как он случился. Драйвер с ошибками может оставить оборудование в непригодном для использования состоянии, оставить ресурсы ядра в неустойчивом состоянии, или, в худшем случае, в случайных местах повредить память ядра. Часто после Oops вы можете просто выгрузить ваш ошибочный драйвер и попробовать ещё раз. Однако, если вы увидите что-то, что позволяет предположить, что система в целом не очень хороша, лучшим решением, как правило, является незамедлительная перезагрузка. hardorsoft 05.12.10 в 08:42 |
||
|
Мы уже говорили, что когда код ядра плохо себя ведёт, на консоль выводится информационное сообщение. Следующий раздел объясняет, как декодировать и использовать такие сообщения. Даже если они выглядят довольно неясными для новичков, процессор выводит всю интересную информацию, часто достаточную, чтобы определить ошибку в программе без необходимости проведения дополнительных тестов. hardorsoft 05.12.10 в 08:42 |
||
|
4.5.1. Сообщения Oops hardorsoft 05.12.10 в 08:43 |
||
|
Большинство ошибок показывают себя в разыменовании указателя NULL или других некорректных значений указателя. Обычным результатом таких ошибок является сообщение Oops. hardorsoft 05.12.10 в 08:43 |
||
|
Почти любой адрес, используемый процессором, представляет собой виртуальный адрес и сопоставляется с физическими адресами через сложную структуру таблиц страниц (исключениями являются физические адреса, используемые в самой подсистеме управления памятью). При разыменовании неверного указателя страничный механизм не может сопоставить указатель с физическим адресом и процессор сигнализирует об ошибке страницы (page fault) в операционной системе. Если адрес не является действительным, ядро не в состоянии загрузить страницу с несуществующим адресом; когда процессор находится в режиме супервизора, оно (обычно) генерирует Oops, если это произошло. hardorsoft 05.12.10 в 08:43 |
||
|
Oops отображает состояние процессора в момент ошибки, в том числе содержание регистров процессора и другую, казалось бы, непонятную информацию. Сообщение создаётся и отправляется с помощью printk в обработчике ошибок (arch/*/kernel/traps.c), как описано выше в разделе 4.2.1. "printk". hardorsoft 05.12.10 в 08:43 |
||
|
Давайте посмотрим на одно из таких сообщений. Вот какие результаты от разыменования указателя NULL на ПК под управлением ядра версии 2.6. Наиболее относящейся к делу информацией здесь является указатель команд (EIP), адрес ошибочной инструкции. hardorsoft 05.12.10 в 08:44 |
||
|
Unable to handle kernel NULL pointer dereference at virtual address 00000000 hardorsoft 05.12.10 в 08:45 |
||
|
Это сообщение было создано записью в устройство, принадлежащее модулю faulty, модуль создан специально для демонстрации ошибки. Реализация метода write из faulty.c тривиальна: hardorsoft 05.12.10 в 08:45 |
||
|
ssize_t faulty_write (struct file *filp, const char _ _user *buf, size_t count, hardorsoft 05.12.10 в 08:45 |
||
|
Как вы можете видеть, мы делаем здесь разыменование NULL указателя. 0 никогда не является допустимым значением указателя, поэтому происходит ошибка, которое ядро превращает в сообщение Oops, показанное ранее. Вызывающий процесс затем убивается. hardorsoft 06.12.10 в 01:26 |
||
|
Модуль faulty имеет другое ошибочное условие в реализации read: hardorsoft 06.12.10 в 01:27 |
||
|
ssize_t faulty_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) hardorsoft 06.12.10 в 01:27 |
||
|
Этот метод копирует строку в локальную переменную; к сожалению, строка больше, чем массив назначения. Переполнение буфера в результате приводит к Oops, когда функция возвращается. Так как инструкция return приносит никуда не указывающий указатель команд, этот вид ошибки гораздо труднее отследить и вы можете получить что-то похожее на это: hardorsoft 06.12.10 в 01:27 |
||
|
EIP: 0010:[<00000000>] hardorsoft 06.12.10 в 01:27 |
||
|
В этом случае мы видим только часть стека вызовов (vfs_read и faulty_read отсутствуют) и ядро жалуется на "плохое значение EIP" (“bad EIP value”). Эта жалоба и адрес вызова (ffffffff), показанный в начале, оба намекают, что был повреждён стек ядра. hardorsoft 06.12.10 в 01:28 |
||
|
В общем, если вы столкнулись с Oops, первое, что нужно сделать, это посмотреть на место, где произошла проблема, которое обычно показывается отдельно от стека вызовов. В первом Oops, показанном выше, соответствующими строками являются: hardorsoft 06.12.10 в 01:28 |
||
|
EIP is at faulty_write+0x4/0x10 [faulty] hardorsoft 06.12.10 в 01:28 |
||
|
Здесь мы видим, что мы были в функции faulty_write, которая находится в модуле faulty (который указан в квадратных скобках). Шестнадцатеричные цифры показывают, что указатель команд имел смещение 4 байта в функции, которая имеет размер 10 (hex) байтов. Часто этого достаточно, чтобы понять, в чём состоит проблема. hardorsoft 06.12.10 в 01:28 |
||
|
Если вам требуется больше информации, стек вызовов покажет вам, как вы попали туда, где всё распалось. Стек печатается в шестнадцатеричной форме; немного поработав, из листинга стека часто можно определить значения локальных переменных и параметров функции. Опытные разработчики ядра могут извлечь пользу из определенного количества распознанных здесь образов; например, если мы посмотрим на распечатку стека из faulty_read Oops: hardorsoft 06.12.10 в 01:28 |
||
|
Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7 hardorsoft 06.12.10 в 01:29 |
||
|
Наконец, при просмотре распечаток Oops всегда будьте бдительны к значениям "отравленных шаблонов" о которых рассказывалось в начале этой главы. Так, например, если вы получите от ядра Oops, в котором вызывающий адрес 0xa5a5a5a5, вы почти наверняка забываете где-то проинициализировать динамическую память. hardorsoft 06.12.10 в 01:29 |
||
|
Учтите, что вы видите символический стек вызовов (как показано выше), только если ваше ядро построено с включенной опцией CONFIG_KALLSYMS. В противном случае, вы увидите простой шестнадцатеричный список, который является гораздо менее полезным, пока вы не расшифровали его другими способами. hardorsoft 06.12.10 в 01:29 |
||
|
4.5.2. Зависания системы hardorsoft 06.12.10 в 01:29 |
||
|
Хотя большинство ошибок в коде ядра заканчивается сообщениями Oops, иногда они могут полностью завесить систему. Если система зависает, сообщение не печатается. Например, если код входит в бесконечный цикл, ядро останавливает планировщик (переключения процессов), и система не реагирует на любые действия, включая специальную комбинацию Ctrl-Alt-Del. Вы имеете два варианта действий с зависаниями системы - либо предотвратить их заранее или заниматься их поиском и устранением постфактум. hardorsoft 06.12.10 в 01:30 |
||
|
(* На самом деле, многопроцессорные системы по-прежнему переключают процессы на других процессорах и даже однопроцессорная машина может переключать, если в ядре включено вытеснение. Однако, для наиболее распространенного случая (однопроцессорная с отключенным вытеснением), система вообще прекращает переключение.) hardorsoft 06.12.10 в 01:34 |
||
|
Вы можете предотвратить бесконечные циклы, вставив в важных точках вызов schedule. Вызов schedule (как вы уже могли догадаться) вызывает планировщик и, следовательно, позволяет другим процессам украсть процессорное время у текущего процесса. Если процесс в пространстве ядра зациклился и-за ошибки в вашем драйвере, вызов schedule позволит вам убить процесс после трассировки произошедшего. hardorsoft 06.12.10 в 01:30 |
||
|
Вы должны знать, конечно, что любой вызов schedule может создать дополнительный источник повторных вызовов драйвера, так как он позволяет работать другим процессам. Этот повторный вход, как правило, не будет проблемой, если предположить, что вы использовали подходящую блокировку в вашем драйвере. Будьте уверены, однако, что не вызываете schedule в то время, когда ваш драйвер удерживает спин-блокировку. hardorsoft 06.12.10 в 01:30 |
||
|
Если ваш драйвер действительно висит в системе и вы не знаете, где вставить вызов schedule, лучшим путём может быть добавление печати каких-нибудь сообщений и выводить их на консоль (изменяя значение console_loglevel, если это необходимо). hardorsoft 06.12.10 в 01:30 |
||
|
Иногда система может казаться зависшей, но это не так. Это может произойти, например, если клавиатура остаётся заблокированной каким-то странным образом. Эти ложные зависания можно обнаружить, глядя на вывод программы, который вы держите работающим только для этой цели. Часы или измеритель системой нагрузки на вашем дисплее - хороший монитор состояния; пока он продолжает обновление, планировщик работает. (hardorsoft) [≈] hardorsoft 06.12.10 в 01:31 |
||
|
Незаменимым инструментом для многих зависаний является "системная кнопка SysRq" (системный запрос), которая доступна на большинстве архитектур. Системный SysRq вызывается комбинацией клавиш Alt и SysRq на клавиатуре компьютера или с помощью других специальных клавиш на других платформах (смотрите для подробностей Documentation/sysrq.txt) и также доступна на последовательной консоли. Третья кнопка, нажатая вместе с этими двумя, выполняет одно из перечисленных полезных действий: hardorsoft 06.12.10 в 01:36 |
||
|
r Отключает режим raw (сырой, возвращение скан-кодов) клавиатуры; полезно в ситуациях, когда порушенное приложение (такое, как X сервер) могло оставить вашу клавиатуру в странном состоянии. hardorsoft 06.12.10 в 01:36 |
||
|
k Вызывает функцию "ключ безопасного внимания", “secure attention key” (SAK). SAK убивает все процессы, запущенные на текущей консоли, оставив вас с чистым терминалом. hardorsoft 06.12.10 в 01:36 |
||
|
s Выполняет аварийную синхронизацию всех дисков. hardorsoft 06.12.10 в 01:36 |
||
|
u Размонтирование. Пытается перемонтировать все диски в режиме "только для чтения". Эта операция обычно вызывается сразу после s, можно сэкономить массу времени на проверку файловой системой в тех случаях, когда система находится в тяжелом состоянии. hardorsoft 06.12.10 в 01:37 |
||
|
b Загрузка. Сразу же перезагружает систему. Будьте уверены, что сначала засинхронизировали и перемонтировали диски. hardorsoft 06.12.10 в 01:37 |
||
|
p Печатает информацию о регистрах процессора. hardorsoft 06.12.10 в 01:37 |
||
|
t Печатает текущий список задач. hardorsoft 06.12.10 в 01:37 |
||
|
m Печатает информацию о памяти. hardorsoft 06.12.10 в 01:37 |
||
|
Существуют и другие функции системного SysRq; смотрите sysrq.txt в каталоге Documentation исходных текстов ядра для полного списка. Обратите внимание, что системный SysRq должен быть явно включен в конфигурации ядра и что большинство дистрибутивов его не разрешает по очевидным соображениям безопасности. Однако, в системе, используемой для разработки драйверов, разрешение системного SysRq стоит того, чтобы собрать себе новое ядро. Magic SysRq может быть отключен во время работы такой командой: hardorsoft 06.12.10 в 01:38 |
||
|
echo 0 > /proc/sys/kernel/sysrq hardorsoft 06.12.10 в 01:38 |
||
|
Вы должны предусмотреть его отключение для предотвращения случайного или намеренного ущерба, если непривилегированные пользователи могут получить доступ к вашей системной клавиатуры. Некоторые предыдущие версии ядра имели sysrq отключенным по умолчанию, так что для его разрешения вам необходимо во время выполнения записать 1 в тот же самый файл в /proc/sys. hardorsoft 06.12.10 в 01:38 |
||
|
Операции sysrq чрезвычайно полезны, так что они делаются доступными для системных администраторов, которые не могут добраться до консоли. Файл /proc/sysrq-trigger является точкой "только для записи", где можно задать определённые действия sysrq, записав соответствующий символ команды; потом вы сможете собрать любые выходные данные из логов ядра. Эта точка входа для sysrq работает всегда, даже если на консоли sysrq отключен. hardorsoft 06.12.10 в 01:38 |
||
|
Если вы столкнулись с "живым зависанием", в котором ваш драйвер застрял в цикле, но система в целом ещё функционирует, полезно знать несколько методов. Зачастую функция p SysRq прямо указывает на виновную процедуру. В противном случае, вы также можете использовать функции профилирования (протоколирования) ядра. Постройте ядро с включенным профилированием и запустите его с profile=2 в командной строке. Сбросьте счётчики профиля утилитой readprofile, а затем отправьте ваш драйвер в цикл. Через некоторое время используйте readprofile снова, чтобы увидеть, на что ядро тратит своё время. Другой, более сложной альтернативой является oprofile, которую вы также можете рассмотреть. Файл Documentation/basic_profiling.txt расскажет вам всё, что необходимо знать, чтобы начать работу с профайлерами. hardorsoft 06.12.10 в 01:39 |
||
|
Ценной предосторожностью при отладке системных зависаний является монтирование всех ваших дисков в режиме "только для чтения" (или их отключение). Если диски находятся в режиме "только для чтения" или демонтированы, нет риска повреждения файловой системы или оставить её в неустойчивом состоянии. Другая возможность заключается в использовании компьютера, который подключает все его файловые системы через NFS, сетевую файловую систему. В ядре должна быть включена возможность "NFS-Root" и во время загрузки должны быть переданы специальные параметры. В этом случае вы избежите повреждения файловой системы, даже не прибегая к SysRq, потому что согласованная файловая система находится под управлением сервера NFS, который не повреждается вашим драйвером устройства. hardorsoft 06.12.10 в 01:39 |
