Главная » Ядро Linux » Функция printk( )

0

Функция  форматированного вывода  сообщений printk( )   работает аналогично библиотечной функции printf( )  языка С.  Действительно в этой  книге  до этого  момента  мы  не  видели  никаких существенных отличий в ее использовании. Для  большинства задач  это  именно так:  функция printk( )  — это  просто  функция ядра, выполняющая форматированный вывод  сообщений. Однако, некоторые различия все же  имеются.

Устойчивость функции printk( )

Одно  из  проверенных и  часто  используемых свойств функции  printk( )  — это ее устойчивость. Функцию print k ()   можно вызывать практически  в любое время и в любом месте ядра.  Её  можно вызывать из  контекста прерывания  и  из  контекста процесса. Её  можно вызывать во  время  удержания блокировки.  Её  можно вызывать одновременно на  нескольких процессорах и  она  не  требует  при  этом  удерживать какие-нибудь блокировки.

Эта  функция очень  устойчива, и  это  очень  важно, потому  что  полезность функции  print k ()   базируется на  том  факте, что  она  всегда  доступна и  всегда  работает.

Неустойчивость функции printk( )

Слабое   место  у функции printk( )   в плане  устойчивости все  же  существует.  Её нельзя использовать до  некоторого момента при  загрузки ядра,  пока  консоль еще не  инициализирована. Действительно, если  нет  консоли, то куда  будут выводится сообщения?

Обычн о  эт о  не  проблема,  если  не  нужно   выполнят ь  отладку   кода,  которы й выполняетс я  на  очен ь  ранни х  стадиях   процесс а  загрузки  (например , функции setup_arch() ,  которая выполняет  инициализацию  специфичную  для  аппаратной платформы).  Отладка такого  рода—  настоящая задача:  отсутствие каких-либо  способов  вывода  сообщений, а только  проблема в полном составе.

В таких  ситуациях тоже  есть  некоторые обнадеживающие моменты, но  их не  много.  Настоящие  хакеры,  которые  работают с  аппаратурой на  таком   низком уровне, для  связи   с  внешним  миром используют аппаратное обеспечение соответствующей платформы, которое всегда  работает (например,  последовательный порт).  Поверьте, что  у большинства людей  такая  работа  не  вызовет радости.  Для  одних  аппаратных платформ такое  решение  работает, для  других  платформ  (включая платформу i386) существуют заплаты кода,  которые тоже  позволяют сэкономить время.

Одно  из  решени й  проблемы — вариант функции  printk(  )  , которы й  может выводить информацию  па  консоль  на  очень   ранних   стадиях   процесса  загрузки early_printk() .  Поведение  этой  функции  аналогично  функции   printk() ,  з а исключением  имени  и  возможности  работать   на  очень   ранних   стадиях   загрузки. Однако, такое  решение не  переносимо, потому  что  не  для  всех  поддерживаемых аппаратных платформ  этот  метод  работы   реализован.  Если  же  он  есть,  то  может  сослужить хорошую службу.

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

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

Главное  отличие между  функциями printk( )   и  printf( )  — это  возможность в первой указывать уровень вывода сообщений ядра (loglevel).  Ядро  использует уровень вывода  сообщений для  принятия решения о том, выводить сообщение на  консоль или нет.  Ядро  выводит на  консоль все  сообщение с уровнями меньшими, или  равными, соответствующему значению  для  консоли  (console   logleyel).  Уровень   вывода  сообщений  можно  указывать следующим образом.

printk(KERN_WARNTNG   "Это   предупреждение!\n") ; printk(KERN_DEBUG  "Это  отладочное  сообщение!\n") ; printk("М ын е  указали   значения  loqlevel!\n "  ) ;

Строки KERN_WARNING  и KERN_DEBUG  определены через  препроцессор в заголовочном файле   <linux/kernel.h> .  Эти  макросы раскрываются в  строки,  соответстпенно "<4> "  и  "<7>" , которые объединяются со  строкой формата в самом  начале сообщения,  выводимого функцией  printk() . После  этого  на основании уровня  выпода  сообщения и  уровня   вывода  консоли  (значение  переменной  console_loglevel ) ядро  принимает решение  выводить информацию  на  консоль или  нет.  В табл.  18.1 приведен полный список возможных значений уровня  вывода  сообщений.

Таблица 18.1. Доступные значения уровня вывода сообщений  ядра (loglevel)

Значение loglevel                        Описание

KERN_EMERG KERN_ALERT KERN_CRIT KERN_ERR KERN_WARNING KERN_NOTICE KERN_INFO

KERN_DEBUG

Аварийная ситуация

Проблема, на которую требуется немедленно обратить внимание

Критическая ситуация Ошибка Предупреждение

Обычная ситуация, но на которую следует обратить внимание

Информационное сообщение

Отладочное сообщение — обычно избыточная информация

Если  уровень   вывода   сообщений  ядра  не  указан,  то  его  значени е  по  умолчанию  равно  DEFAULT_MESSAGE_LOGLEVEL,  который в данный момент  равен  KERN_ WARNING. Так  как  это  значение может  измениться, то для  своих  сообщений необходимо  всегда  указывать уровень   вывода.

Наиболее важный уровень вывода— KERN_EMERG определен как  "<0>", а наименее важный — KERN_DEBUG, как  "<7>" . Например, после  обработки препроцессором кода  из  предыдущего примера получается  следующее.

printk("<4>Этo предупреждение!\n"); printk("<7>Это отладочное сообщение!\n"); printk("<4>Мы не указали значения loglevel!\n");

Как  вы  будете  использовать функцию printk( )  зависит только  от вас.  Конечно, обычные  сообщения,  которые  должны быть  видимы,  должны иметь  соответствующий  уровень вывода.  Отладочные сообщения,  которы е  в  большом  количестве встраиваются в  самые  разные места  кода  с  целью  разобраться с  проблемой — "допустим   ошибка  здесь",  "пробуем", "работает"   могут  иметь  любой  уровень вывода. Один  вариант — оставить уровень при  котором сообщения  выводятся на  консоль равным значению этого  параметра по  умолчанию,  а уровень вывода  ваших  сообщений  установить в значение KERN_CRIT,  или  что-то  около  этого.  Можно поступить и наоборот— для  отладочных сообщений установить уровень KERN_DEBUG  и  поднять уровень при  котором сообщения выводятся на консоль. Каждый из  вариантов имеет свои  положительные и отрицательные стороны — вам  решать.

Уровни вывода  сообщений  определены в  файле   <linux/kernel.h> .

Буфер сообщений ядра

Сообщения ядра  хранятся в кольцевом буфере  (log buffer)  размером LOG_BUF_LEN. Этот  размер  можно изменять во  время  компиляции с помощью параметра CONFIG_ LOG_BUF_SHIFT. Для  однопроцессорной машины это  значение по умолчанию равно

16  Кбайт. Другими словами в ядре  может  хранится до  16 Кбайт системных сообщений.   Если  общий размер  всех  сообщений ядра  достигает этого  максимального значения и  приходит новое  сообщение, то  оно  переписывается поверх  самого  старого из  хранящихся в  буфере  сообщений. Буфер  сообщений ядра  называется  кольцевым, потому  что  запись и  считывание сообщений выполняется по  круговой схеме.

Использование  кольцевого  буфера   предоставляет определенные  преимущества. Так  как  одновременные операции чтения и записи в кольцевом буфере  выполняются достаточно просто, то функцию printk( )  можно использовать даже  из  контекста прерывания. Более  того, это  позволяет просто   организовать управление системными  сообщениями. Если  сообщений оказывается очень  много, то  новые  сообщения просто   затирают старые.   Если  возникает проблема,  которая проявляется в генерации  большого количества сообщений,  то  буфер  сообщений просто   начинает переписывать себя  вместо  того,  чтобы  бесконтрольно занимать память. Единственный недостаток кольцевого буфера  — возможность потерять сообщения,  что  не  такая  уж и большая плата  за  ту устойчивость,  которую такое  решение предоставляет.

Демоны syslogd и klogd

В  стандартной системе Linux  для  извлечения сообщений ядра  из  буфера  используется  специальный демон  пространства пользователя  klogd , который направляет эти  сообщения в  файл  журнала   системных сообщений.  Для  чтения системных сообщений  программа  klog d  может  считывать данные  из  файла   /proc/kmsg ,  или использовать  системный  вызов   syslog() .  По  умолчанию используется  подход  на

основе файловой системы  /рrоc.  Если  сообщений нет, то демон  klog d блокируется на  операции чтения, пока  не  поступит новое  сообщение. Когда  приходит новое  сообщение, демон  возвращается к выполнению, считывает сообщения и обрабатывает их.  По  умолчанию сообщения  отправляются демону   syslogd .

Демон   syslog d  добавляет полученные  сообщения  в  конец файла   журнала,  по умолчанию —  /var/log/messages .  Имя   соответствующего  файла   можно настроить в  конфигурационном  файле    /etc/syslog.conf .

Изменить  уровень вывода  сообщений на  консоль  (console  loglevel)   можно при старте  демона klog d  с  помощью флага   -с.

Замечание относительно функции printk( ) и разработки ядра

Когда  впервые  начинают  разрабатывать код  ядра,  то  скорее   всего  очень   часто приходится заменять функцию printf( )  на функцию printk() . Это  нормально, потому  что  нельзя не  принимать во  внимание многолетний опыт  по написанию пользовательских программ и использовании  функции  printf() .  Следует  надеяться, что повторение таких  ошибок не  будет  продолжаться долго, потому  что  повторяющиеся ошибки компоновщика начнут  быстро  надоедать.

Однажды вдруг  окажется,  что  вы  поймали себя  на  том, что  начали использовать функцию printk(  )   вместо   функции  printf( )   в  пользовательских  программах. Когда  для  вас  этот  день  наконец наступит, то  можно сказать, что  вы  стали  настоящим  хакером и  специалистом по  разработке кода  ядра.

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии