Главная страница
Навигация по странице:

  • Неустойчивость функции p r i n t k ( )

  • Уровни вывода сообщений ядра

  • Таблица 18.1.

  • Второе издание


    Скачать 3.09 Mb.
    НазваниеВторое издание
    Дата08.09.2019
    Размер3.09 Mb.
    Формат файлаpdf
    Имя файлаLav_Robert_Razrabotka_yadra_Linux_Litmir.net_264560_original_254.pdf
    ТипДокументы
    #86226
    страница46 из 53
    1   ...   42   43   44   45   46   47   48   49   ...   53
    18
    Отладка
    О
    дин из самых существенных факторов, который отличает разработку ядра от разработки пользовательских приложений, — это сложность отладки.
    Отлаживать код ядра сложно, но крайней мере по сравнению с кодом пространства пользователя. Еще больше усугубляет ситуацию тот факт, что ошибка в ядре может привести к катастрофическим последствиям для всей системы.
    Успех в освоении приемов отладки ядра и, в конце концов, в разработке ядра во- обще, в основном, зависит от опыта и понимания принципов работы операционной системы в целом. Понятно также, что, для того чтобы успешно выполнять отладку ядра, необходимо понимать, как ядро работает. Тем не менее когда-то нужно начать,
    и в этой главе будут рассмотрены подходы к отладке ядра.
    С чего необходимо начать
    Итак, готовы ли вы начать охоту за ошибками? Этот путь может оказаться длин- ным и полным разочарований. Некоторые ошибки ставили в тупик все сообщество разработчиков ядра на несколько месяцев. К счастью, на каждую из таких злостных ошибок находятся простые, которые легко исправить. Если вам повезет, то все про- блемы, с которыми вы столкнетесь, будут простыми и тривиальными. Однако чтобы это проверить, необходимо начать исследования. Для этого понадобится следую- щее.
    • Сама проблема. Может звучать глупо, но дефект должен быть конкретным и хорошо определенным. Очень помогает, если его хотя бы кто-нибудь может устойчиво воспроизвести. Однако, к сожалению, дефекты обычно ведут себя не так хорошо, как хотелось бы, и не всегда могут быть хорошо определены.
    • Версия ядра, в которой существует дефект (обычно это последняя версия, хотя кто может это гарантировать?). Еще лучше, если известна версия ядра, в кото- рой проблема впервые появилась. Мы рассмотрим, как это установить, если нет такой информации.
    • Немного удачи, опыта и их комбинации.
    Если дефект нельзя воспроизвести, то многие из приведенных ниже подходов становятся бесполезными. Очень важно, чтобы проблему можно было повторить.
    Если этого не удается сделать, то исправление дефекта становится возможным толь- ко путем визуального анализа кода для того, чтобы найти в нем ошибку. На самом
    деле так случается достаточно часто (например, с разработчиками ядра), но очевид- но, что шансы добиться успеха становятся более весомыми, если появляется возмож- ность воспроизвести проблему.
    Может также показаться странным, что существуют дефекты, которые кто-то не может воспроизвести. Дело в том, что в пользовательских программах дефекты чаще всего проявляются очень просто, например вызов функции foo приводит к соз-
    данию файла core. В ядре все совсем по-друтому. Взаимодействия между ядром, про- странством пользователя и аппаратурой могут быть достаточно тонкими. Состояния конкуренции за ресурсы могут возникать с вероятностью одно на миллион итераций алгоритма. Плохо спроектированный или даже не правильно скомпилированный код может обеспечивать удовлетворительную производительность на одной систе- ме, но неудовлетворительную на другой, Очень часто происходит так, что на ка- кой-то случайной машине, при очень специфическом характере загрузке, начинают проявляться дефекты, которые больше нигде не проявляются. Чем больше доступ- но дополнительной информации при локализации дефекта, тем лучше. Во многих случаях, как только удалось устойчиво воспроизвести проблему, можно считать, что большая половина работы сделана.
    Дефекты ядра
    Дефекты в ядре могут быть такими же разнообразными, как и дефекты в поль- зовательских программах. Они возникают по различным причинам и проявляются в разнообразных формах. Дефекты занимают диапазон от явно неправильного кода
    (например, запись правильного значения в неправильное место) до ошибок синхро- низации (например, если не правильно блокируется совместно используемая пере- менная). Эти дефекты проявляются в любой форме; от плохой производительности до неправильного функционирования и даже до потери данных.
    Часто, между тем моментом, когда в ядре возникла ошибка и тем моментом, ког- да пользователь ее заметил происходит большая цепь событий. Например, разделя- емая структура данных, у которой нет счетчика использования может привести к возникновению состояния конкуренции за ресурс (race condition). Если не принять необходимых мер, то один процесс может освободить память, в которой хранится структура, в то время, как другой процесс может эту структуру все еще использовать.
    Спустя некоторое время второй процесс может обратиться к этим данным, что в свою очередь может привести к попытке разыменования указателя со значением
    NULL, если будут считаны случайные данные ("мусор"), или вообще не привести ни к чему плохому (если данные в соответствующей области памяти еще не были переза- писаны). Разыменование указателя со значением NULL приводит к выводу сообщения "oops", в то время, как случайный "мусор" может привести к потере данных (и со- ответственно к неправильному функционированию, или опять же к выводу сообще- ния "oops", но уже по другом поводу). Пользователь же заметит только неправиль- ное функционирование или сообщение "oops". Разработчик ядра при этом должен пойти но обратному пути: исходя из ошибки определить, что к данным было обра- щение после того, как память с этими данными была освобождена, что это произо- шло в результате возникновения конкуренции за ресурс и исправить ошибку путем правильного учета количества ссылок на совместно используемую структуру данных.
    Для этого также вероятно потребуется применение блокировок.
    374 Глава 18

    Отладка ядра может показаться сложным делом, тем не менее, ядро не особо от- личается от других больших программных проектов. У ядра есть свои уникальные особенности, такие как ограничения связанные со временем выполнения участков кода, возможности возникновения состояний конкуренции (race) — как результат параллельного выполнения множества потоков в ядре. Можно дать стопроцентную гарантию, что если приложить некоторые усилия и понимание, то проблемы ядра можно с успехом находить и решать (и даже, возможно, получать удовольствие от успешного преодоления трудностей).
    Функция p r i n t k ( )
    Функция форматированного вывода сообщений p r i n t k ( ) работает аналогично библиотечной функции p r i n t f ( ) языка С. Действительно в этой книге до этого мо- мента мы не видели никаких существенных отличий в ее использовании. Для боль- шинства задач это именно так: функция p r i n t k ( ) — это просто функция ядра, вы- полняющая форматированный вывод сообщений. Однако, некоторые различия все же имеются.
    Устойчивость функции p r i n t k ( )
    Одно из проверенных и часто используемых свойств функции p r i n t k ( ) — это ее устойчивость. Функцию p r i n t k () можно вызывать практически в любое время и
    в любом месте ядра. Её можно вызывать из контекста прерывания и из контекста процесса. Её можно вызывать во время удержания блокировки. Её можно вызывать одновременно на нескольких процессорах и она не требует при этом удерживать ка- кие-нибудь блокировки.
    Эта функция очень устойчива, и это очень важно, потому что полезность функ- ции p r i n t k () базируется на том факте, что она всегда доступна и всегда работает.
    Неустойчивость функции p r i n t k ( )
    Слабое место у функции p r i n t k ( ) в плане устойчивости все же существует. Её
    нельзя использовать до некоторого момента при загрузки ядра, пока консоль еще не инициализирована. Действительно, если нет консоли, то куда будут выводится со- общения?
    Обычно это не проблема, если не нужно выполнять отладку кода, который выполняется на очень ранних стадиях процесса загрузки (например, функции s e t u p _ a r c h ( ) , которая выполняет инициализацию специфичную для аппаратной платформы). Отладка такого рода— настоящая задача: отсутствие каких-либо спосо- бов вывода сообщений, а только проблема в полном составе.
    В таких ситуациях тоже есть некоторые обнадеживающие моменты, но их не мно- го. Настоящие хакеры, которые работают с аппаратурой на таком низком уровне,
    для связи с внешним миром используют аппаратное обеспечение соответствующей платформы, которое всегда работает (например, последовательный порт). Поверьте,
    что у большинства людей такая работа не вызовет радости. Для одних аппаратных платформ такое решение работает, для других платформ (включая платформу i386)
    существуют заплаты кода, которые тоже позволяют сэкономить время.
    Отладка 375

    Одно из решений проблемы — вариант функции p r i n t k ( ) , который может выводить информацию па консоль на очень ранних стадиях процесса загрузки - e a r l y _ p r i n t k ( ) . Поведение этой функции аналогично функции p r i n t k ( ) , за исключением имени и возможности работать на очень ранних стадиях загрузки.
    Однако, такое решение не переносимо, потому что не для всех поддерживаемых ап- паратных платформ этот метод работы реализован. Если же он есть, то может со- служить хорошую службу.
    Кроме ситуаций, когда необходимо выводить на консоль информацию на очень ранних стадиях загрузки системы, можно положиться на функцию p r i n t k ( ) , кото- рая работает практически всегда.
    Уровни вывода сообщений ядра
    Если уровень вывода сообщений ядра не указан, то его значение по умолча- нию равно DEFAULT_MESSAGE_LOGLEVEL, который в данный момент равен KERN_
    WARNING. Так как это значение может измениться, то для своих сообщений необхо- димо всегда указывать уровень вывода.
    3 7 6 Глава 18
    Главное отличие между функциями p r i n t k ( ) и p r i n t f ( ) — это возможность в первой указывать уровень вывода сообщений ядра (loglevel). Ядро использует уровень вы- вода сообщений для принятия решения о том, выводить сообщение на консоль или нет. Ядро выводит на консоль все сообщение с уровнями меньшими, или равными,
    соответствующему значению для консоли (console logleyel). Уровень вывода сообще- ний можно указывать следующим образом.
    printk(KERN_WARNTNG "Это предупреждение!\n");
    printk(KERN_DEBUG "Это отладочное сообщение!\n");
    p r i n t k ( " М ы не указали значения l o q l e v e l ! \ n " );
    Строки KERN_WARNING и KERN_DEBUG определены через препроцессор в заголо- вочном файле < l i n u x / k e r n e l . h > . Эти макросы раскрываются в строки, соответ- стпенно "<4>" и "<7>", которые объединяются со строкой формата в самом начале сообщения, выводимого функцией p r i n t k ( ) . После этого на основании уровня выпо- да сообщения и уровня вывода консоли (значение переменной c o n s o l e _ l o g l e v e l )
    ядро принимает решение выводить информацию на консоль или нет. В табл. 18.1
    приведен полный список возможных значений уровня вывода сообщений.
    Таблица 18.1. Доступные значения уровня вывода сообщений ядра (loglevel)
    Значение loglevel Описание
    KERN_EMERG
    KERN_ALERT
    KERN_CRIT
    KERN_ERR
    KERN_WARNING
    KERN_NOTICE
    KERN_INFO
    KERN_DEBUG
    Аварийная ситуация
    Проблема, на которую требуется немедленно обратить внимание
    Критическая ситуация
    Ошибка
    Предупреждение
    Обычная ситуация, но на которую следует обратить внимание
    Информационное сообщение
    Отладочное сообщение — обычно избыточная информация

    Наиболее важный уровень вывода— KERN_EMERG определен как "<0>", а наиме- нее важный — KERN_DEBUG, как "<7>". Например, после обработки препроцессором кода из предыдущего примера получается следующее.
    printk("<4>Этo предупреждение!\n");
    printk("<7>Это отладочное сообщение!\n");
    printk("<4>Мы не указали значения loglevel!\n");
    Как вы будете использовать функцию p r i n t k ( ) зависит только от вас. Конечно,
    обычные сообщения, которые должны быть видимы, должны иметь соответству- ющий уровень вывода. Отладочные сообщения, которые в большом количестве встраиваются в самые разные места кода с целью разобраться с проблемой — "до- пустим ошибка здесь", "пробуем", "работает" - могут иметь любой уровень вывода.
    Один вариант — оставить уровень при котором сообщения выводятся на консоль равным значению этого параметра по умолчанию, а уровень вывода ваших сообще- ний установить в значение KERN_CRIT, или что-то около этого. Можно поступить и наоборот— для отладочных сообщений установить уровень KERN_DEBUG и поднять уровень при котором сообщения выводятся на консоль. Каждый из вариантов имеет свои положительные и отрицательные стороны — вам решать.
    Уровни вывода сообщений определены в файле < l i n u x / k e r n e l . h > .
    Буфер сообщений ядра
    Сообщения ядра хранятся в кольцевом буфере (log buffer) размером LOG_BUF_LEN.
    Этот размер можно изменять во время компиляции с помощью параметра CONFIG_
    LOG_BUF_SHIFT. Для однопроцессорной машины это значение по умолчанию равно
    16 Кбайт. Другими словами в ядре может хранится до 16 Кбайт системных сообще- ний. Если общий размер всех сообщений ядра достигает этого максимального зна- чения и приходит новое сообщение, то оно переписывается поверх самого старого из хранящихся в буфере сообщений. Буфер сообщений ядра называется кольцевым,
    потому что запись и считывание сообщений выполняется по круговой схеме.
    Использование кольцевого буфера предоставляет определенные преимущества.
    Так как одновременные операции чтения и записи в кольцевом буфере выполняют- ся достаточно просто, то функцию p r i n t k ( ) можно использовать даже из контекста прерывания. Более того, это позволяет просто организовать управление системны- ми сообщениями. Если сообщений оказывается очень много, то новые сообщения просто затирают старые. Если возникает проблема, которая проявляется в генера- ции большого количества сообщений, то буфер сообщений просто начинает пере- писывать себя вместо того, чтобы бесконтрольно занимать память. Единственный недостаток кольцевого буфера — возможность потерять сообщения, что не такая уж и большая плата за ту устойчивость, которую такое решение предоставляет.
    Демоны syslogd и klogd
    В стандартной системе Linux для извлечения сообщений ядра из буфера исполь- зуется специальный демон пространства пользователя klogd, который направляет эти сообщения в файл журнала системных сообщений. Для чтения системных со- общений программа k l o g d может считывать данные из файла / p r o c / k m s g , или использовать системный вызов s y s l o g ( ) . По умолчанию используется подход на
    Отладка 377
    основе файловой системы /рrоc. Если сообщений нет, то демон klogd блокируется на операции чтения, пока не поступит новое сообщение. Когда приходит новое со- общение, демон возвращается к выполнению, считывает сообщения и обрабатывает их. По умолчанию сообщения отправляются демону syslogd.
    Демон s y s l o g d добавляет полученные сообщения в конец файла журнала, по умолчанию — /var/log/messages. Имя соответствующего файла можно настроить в конфигурационном файле / e t c / s y s l o g . c o n f .
    Изменить уровень вывода сообщений на консоль (console loglevel) можно при старте демона klogd с помощью флага -с.
    Замечание относительно функции p r i n t k ( ) и разработки ядра
    Когда впервые начинают разрабатывать код ядра, то скорее всего очень часто приходится заменять функцию p r i n t f ( ) на функцию p r i n t k ( ) . Это нормально, по- тому что нельзя не принимать во внимание многолетний опыт по написанию поль- зовательских программ и использовании функции p r i n t f ( ) . Следует надеяться, что повторение таких ошибок не будет продолжаться долго, потому что повторяющиеся ошибки компоновщика начнут быстро надоедать.
    Однажды вдруг окажется, что вы поймали себя на том, что начали использовать функцию p r i n t k ( ) вместо функции p r i n t f ( ) в пользовательских программах.
    Когда для вас этот день наконец наступит, то можно сказать, что вы стали настоя- щим хакером и специалистом по разработке кода ядра.
    Сообщения Oops
    Сообщения oops — обычный для ядра способ сообщить пользователю, что произо- шло что-то нехорошее. Так как ядро управляет всей системой, то оно не может само себя исправить, или завершить, как это возможно для программ пространства поль- зователя, когда они делают что-то не так. Вместо этого, ядро выводит сообщение oops. Такое сообщение включает вывод информации об ошибке на консоль, вывод дампа содержимого всех регистров и вывод обратной трассировки вызовов функций
    (back trace). Сбои в работе ядра трудно обработать, поэтому ядро должно "пролезть''
    через многие дыры, чтобы вывести сообщение oops и выполнить за собой все не- обходимые действия по очистке. Часто после выдачи сообщения oops ядро нахо- дится в несогласованном состоянии. Например, в момент возникновения ситуации,
    в которой выдается сообщение oops, ядро может находится в процессе обработки важных данных. В этот момент может удерживаться блокировка, или выполняться сеанс взаимодействия с оборудованием. Ядро должно аккуратно отойти от текуще- го состояния и попытаться восстановить контроль над системой. Во многих случаях это невозможно. Если ситуация, в которой выдается сообщение oops, возникает в контексте прерывания, то ядро не может продолжать работу и переходит в состо- яние паники. Состояние паники проявляется в полной остановке системы. Если oops возникает в холостой задаче (idle task, идентификатор p i d равен нулю), или при выполнении процесса i n i t (идентификатор p i d равен единице), то ядро также переходит в состояние паники, потому что ядро не может продолжать выполнение
    378 Глава 18
    без этих важных процессов. Однако, если oops возникает при выполнении любого другого процесса, то ядро завершает этот процесс и продолжает работу.
    Сообщение oops может выдаваться по многим причинам, включая недопустимый доступ к памяти (memory access violation) и выполнение недопустимой машинной команды. Как разработчику ядра, вам придется иметь дело с сообщениями oops и далее, несомненно, быть причиной их появления.
    Ниже показано сообщение oops для машины аппаратной платформы РРС, кото- рое возникло и обработчике таймера для сетевого интерфейсного адаптера tulip.
    Oops: Exception in kernel mode, sig: 4
    Unable to handle kernel NULL pointer dereference at virtual address 00000001
    NIP: C013A7F0 LR: C013A7F0 SP: C0685E00 REGS: c0905dl0 TRAP: 0700
    Not tainted
    MSR: 00089037 ЕЕ: 1 PR: 0 FP: 0 ME: 1 IR/DR: 11
    TASK = c0712530[0] swapper Last syscall: 120
    GPROO: C013A7C0 C0295E00 C0231530 0000002F 00000001 C0380CB8 C0291B80 C02D0000
    GPR08: 000012AO 00000000 00000000 C0292AA0 4020A088 00000000 00000000 00000000
    GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    GPR24: 00000000 00000005 00000000 00001032 C3F7C000 00000032 FFFFFFFF C3F7C1C0
    Call trace:
    [c013ab30] tulip_timer+0xl28/0xlc4
    [c0020744] run_timer_softirq+0xl0c/0xl64
    [c001b864] do_softirq+0x88/0xl04
    [c0007e80] timer_mterrupt+0x284/0x298
    [c00033c4] ret_from_except+0x0/0x34
    [c0007b84] default_idle+0x20/0x60
    [c0007bf8] cpu_idle+0x34/0x38
    [c0003ae8] rest_init+0x24/0x34
    У пользователей ПК может вызвать удивление количество регистров процессора
    (32 - огромное число!). Сообщение oops для аппаратной платформы х86, которые возможно вам более знакомы, имеют несколько более простой вид. Тем не менее,
    важная информация идентична для всех аппаратных платформ: содержимое всех ре- гистров и обратная трассировка.
    Обратная трассировка показывает точную последовательность вызовов функ- ций, которая привела к проблеме. В данном случае можно точно определить, что случилось: машина выполняла холостое задание - холостой цикл: вызов функ- ции c p u _ i d l e ( ) , из которой циклически вызывается функция d e f a u l t _ i d l e ( ) .
    Поступило прерывание от системного таймера, в котором вызываются обра- ботчики таймеров ядра. Среди них вызывается обработчик таймера — функция t u l i p _ t i m e r ( ) , в которой выполнено разыменование указателя со значением NULL.
    Можно даже воспользоваться значением смещения (числа вроде 0х128/0х1с4, кото- рые указаны справа от имени функции) для точного нахождения команды, в кото- рой возникла ошибка.
    Содержимое регистров точно также полезно, хотя и используется не так часто.
    Вместе с дизассемблированным кодом функции содержимое регистров может по- мочь восстановить точную последовательность событий, которая привела к пробле- ме. Если значение в некотором регистре не соответствует ожидаемому, то это может пролить некоторый свет на корень проблемы. В данном случае можно проверить,
    какие регистры содержат значение NULL (все разряды нулевые) и определить, какая
    Отладка 379
    из переменных функции содержит не то значение. В ситуациях, похожих на данную,
    скорее всего причина — конкуренция за ресурс (race) и скорее всего между тайме- ром и другой частью сетевого адаптера. Отладка состояний конкуренции за ресур- сы — всегда серьезная задача.
    Утилита ksymoops
    Только что рассмотренное сообщение oops имеет так называемый декодированный
    вид, потому что адреса памяти транслированы в имена функций, которые им соот- ветствуют. Не декодированный вид предыдущего сообщения выглядит следующим образом.
    NIP: C013A7F0 LR: C013A7F0 SP: C0685E00 REGS: c0905dl0 TRAP: 0700
    Not tainted
    MSR: 00089037 ЕЕ: 1 PR: 0 FP: 0 ME 1 IR/DR: 11
    TASK = c0712530[0] 'swapper' Last syscall: 120
    GPROO: C013A7CO C0295E00 C0231530 0000002F 00000001 C0380CB8 C0291B80 C02D0000
    GPR08: 000012AO 00000000 00000000 C0292AA0 4020A088 00000000 00000000 00000000
    GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    GPR24: 00000000 00000005 00000000 00001032 C3F7C000 00000032 FFFFFFFF C3F7C1C0
    Call trace: [c013ab30] [c0020744] [c001b864] [c0007e80] [c00061c4]
    [c0007b84] [c0007bf8] [c0003ae8]
    Адреса обратной трассировки должны быть переведены в символические име- на функций. Это можно сделать с помощью команды ksymoops при наличии файла
    System.map, который сгенерирован во время компиляции данного ядра. Если ис- пользуются загружаемые модули ядра, то необходима также информация о модулях.
    Утилита ksymoops пытается самостоятельно определить всю необходимую информа- цию, поэтому обычно ее можно просто вызывать следующим образом.
    ksymoops saved_oops.txt
    Программа выводит декодированную информацию сообщения oops. Если инфор- мация, которая используется по умолчанию, недоступна, или есть необходимость указать альтернативное положение соответствующих информационных файлов, то на такой случай программа принимает различные параметры. Страницы руковод- ства по данной программе, которые необходимо прочитать перед использованием,
    содержат всю необходимую информацию.
    Программа ksymoops включена в большинство поставок операционной системы
    Linux.
    Функция kallsyms
    К счастью, больше нет необходимости использовать программу ksymoops. Это очень полезно, потому что, хотя, у разработчиков обычно нет проблем с ее исполь- зованием, пользователи часто указывают неправильный файл System.map, или не- правильно декодируют сообщение oops.
    В разрабатываемой серии ядра 2.5 была введено новая возможность k a l l s y m s ,
    которая включается с помощью конфигурационного параметра CONFIG_KALLSYMS.
    Эта функция включает в исполняемый образ ядра информацию для отображения адресов памяти в соответствующие имена функций ядра, что дает возможность ядру
    380 Глава 18
    самостоятельно декодировать информацию обратной трассировки. Следовательно,
    декодирование сообщений oops больше не требует файла System.map, или утилиты kallsyms. Как недостаток такого подхода следует отметить некоторое увеличение объема памяти, используемой ядром, так как таблица перевода адресов памяти в имена функций загружается в постоянно отображаемую память ядра. На такое уве- личение объемов используемой памяти стоит пойти, по крайней мере, на этапе раз- работки ядра.
    Конфигурационные параметры отладки ядра
    Существует несколько конфигурационных параметров, которые помогают в от- ладке и тестировании кода ядра и которые включаются во премя компиляции. Эти параметры доступны в пункте Kernel hacking меню редактора конфигурации ядра.
    Все эти параметры зависят от параметра CONFIG_DEBUG_KERNEL. Для разработки кода ядра следует включать только те параметры, которые необходимы.
    Некоторые из этих параметров достаточно полезны, такие как отладка работы со слябовым распределителем памяти (slab layer debugging), отладка работы с верхней памятью (high memory debugging), отладка работы с отображаемым на память вво- дом-выводом (I/O mapping debugging), отладка работы со спин-блокировками (spin- lock debugging) и проверка переполнения стека (stack overflow checking). Однако,
    один из самых полезных параметров — это проверка перехода в состояние ожидания при
    захваченной спин-блокировке (sleep-inside-spinlock checking), которая на самом деле выполня- ет значительно больше работы.
    Отладка атомарных операций
    Начиная с серии 2.5 в ядре появилась отличная инфраструктура для определе- ния всех типов нарушения атомарности. Вспомните из главы 8, "Введение в синхро- низацию выполнения кода ядра", что атомарность означает неделимое выполнение,
    то есть код выполняется без перерыва до завершения, или не завершается вообще.
    Код, который удерживает спин-блокировку, или выполняется при запрещенной пре- емптивности ядра, является атомарным. Во время атомарного выполнения нельзя переходить в состояние ожидания. Ожидание при удерживаемой спин-блокировке —
    один из вариантов взаимоблокировки.
    Благодаря свойствам преемптивности, ядро имеет глобальный счетчик преем- птивности. Ядро может быть настроено так, что, если выполняется переход в состо- яние ожидания, или даже выполняется код, который потенциально может перехо- дить в состояние ожидания при выполнении атомарной операции, то ядро выводит предупреждающее сообщение и обратную трассировку. Потенциальные ошибки,
    которые детектируются таким образом, включают вызов функции schedule () при удерживаемой блокировке, выполнение блокирующего выделения памяти при удер- живаемой блокировке, или переход в состояние ожидания при удерживаемой ссылке на данные, связанные с процессором. Эта отладочная инфраструктура может обнару- жить очень много ошибок и ее очень рекомендуется использовать.
    Следующие конфигурационные параметры позволяют полностью использовать данную возможность.
    Отладка 381

    CONFIG_PREEMPT=y
    CONFIG_DEBUG_KERNEL=y
    CONFIG_KALLSYMS=y
    CONFIG_SPINLOCK_SLEEP=y
    Генерация ошибок и выдача информации
    Существует несколько подпрограмм ядра, которые позволяют легко сигнали- зировать о наличии дефектов кода, обеспечивать объявления об ошибках и выво- дить необходимую информацию. Две наиболее часто используемые — это BUG() и
    BUG_ON(). При вызове эти функции создают ситуацию oops, которая проявляется в выводе обратной трассировки стека ядра и сообщения об ошибке. Каким обра- зом эти вызовы генерируют ситуацию oops зависит от аппаратной платформы. Для большинства аппаратных платформ вызовы BUG() и BUG_ON() определяются как некоторая недопустимая машинная команда, которая приводит к выводу желаемого сообщения oops.
    Обычно эти вызовы используются в качестве объявления о наличие ошибки
    (assertion), чтобы сигнализировать о ситуации, которая не должна произойти.
    if (bad_thing)
    BUG();
    Или даже так.
    BUG_ON(bad_thing);
    О более критичной ошибке можно сигнализировать с помощью функции panic ().
    Функция p a n i c () печатает сообщение об ошибке и останавливает ядро. Ясно, что эту функцию следует использовать только в самой плохой ситуации.
    if (terrible_thing)
    panic("foo is %ld!\n", foo);
    Иногда необходимо просто вывести на консоль трассировку стека, чтобы облег- чить отладку. В этих случаях используется функция dump_stack(). Эта функция ото- бражает на консоль содержимое регистров процессора и обратную трассировку вы- зовов функций.
    if (!debug_check) {
    printk(KERN_DEBUG "выдать некоторую информацию . . . \ n " ) ;
    dump_stack();
    }
    Магическая клавиша SysRq
    Использование магической клавиши SysRq, которую можно активизировать с по- мощью конфигурационного параметра CONFIG_MAGIC_SYSRQ на этапе компиляции,
    часто позволяет значительно облегчить жизнь. Клавиша SysRq является стандарт- ной на многих клавиатурах. Для аппаратных платформ i386 и РРС ей соответствует комбинация клавиш ALT-PrintScreen. Если указанный конфигурационный пара- метр активизирован, то специальные комбинации клавиш позволяют взаимодейство-
    382 Глава 18
    вать с ядром независимо от того, чем ядро в данный момент нанимается. Это в свою очередь позволяет выполнять некоторые полезные операции даже на неработоспо- собной системе.
    В дополнение к конфигурационному параметру существует вызов s y s c t l для включения и выключения этого свойства.
    echo 1 > /proc/sys/kernel/sysrq
    Список возможных комбинаций клавиш можно получить с консоли путем на- жатия комбинации клавиш SysRq-h. Комбинация клавиш SysRq-s выполняет синхронизацию не сохраненных буферов файловых систем на диск, комбинация
    SysRq-u размонтирует все файлопые системы, a SysRq-b — перегружает машину.
    Последовательное использование этих комбинаций клавиш позволяет более безопас- но перегрузить машину, которая зависла, чем простое нажатие кнопки r e s e t .
    Если машина заблокирована очень сильно, то она может не отвечать на магиче- ские комбинации клавиш SysRq, или соответствующая операция не будет выполне- на. Если же повезет, то эти комбинации клавиш смогут помочь при отладке, а также сохранить данные. В табл. 18.2 приведен список поддерживаемых команд SysRq.
    1   ...   42   43   44   45   46   47   48   49   ...   53


    написать администратору сайта