Wiki

Глава 9. Замена printk

9.1. Замена printk

Ранее я уже говорил о том, что XWindow и разработка модулей ядра есть вещи несовместимые. Это все так, но иногда возникает необходимость выдачи сообщений от модуля на любой tty. [13]

В качестве одного из вариантов можно предложить следующее: используя указатель на текущий исполняемый процесс -- current, получить структуру tty. Затем извлечь из этой структуры указатель на функцию вывода строки и использовать ее для выдачи сообщений.

Пример 9-1. print_string.c

/* 
 *  print_string.c - отправляет вывод на tty терминала, независимо от того 
 *  X11 это, или telnet, или что-то еще. Делается это путем вывода строки на tty,
 *  ассоциированный с текущим процессом.
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>   /* определение current */
#include <linux/tty.h>     /* определение tty */
#include <linux/version.h> /* макрос LINUX_VERSION_CODE */
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter Jay Salzman");
 
static void print_string(char *str)
{
  struct tty_struct *my_tty;
 
  /* 
   * Начиная с версии 2.6.6, структура tty перекочевала в структуру signal 
   */
#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,5) )
  /* 
   * tty текущего процесса
   */
  my_tty = current->tty;
#else
  /* 
   * tty текущего процесса, для ядер 2.6.6 и выше
   */
  my_tty = current->signal->tty;
#endif
 
  /* 
   * если my_tty == NULL, то текущий процесс не имеет tty на который можно 
   * было бы что нибудь вывести (например, демон).  
   * В этом случае нам ничего не нужно делать.
   */
  if (my_tty != NULL) {
 
    /* 
     * my_tty->driver -- структура, которая хранит указатели на функции-обработчики,
     * одна из которых (write) используется для вывода строк на tty. 
     *
     * Первый параметр функции -- tty, на который осуществляется вывод,
     * поскольку эта функция обычно используется для вывода на все 
     * tty одного и того же типа. 
     * Второй параметр -- флаг расположения строки
     * если строка находится в пространстве ядра, флаг равен false (0)
     * если в пространстве пользователя, то true (не ноль).  
     * Третий параметр -- указатель на строку.
     * Четвертый параметр -- длина строки.
     */
    ((my_tty->driver)->write) (my_tty,  /* Собственно tty  */
             0,                         /* Строка в пространстве ядра */
             str,                       /* Сама строка */
             strlen(str));              /* Длина строки */
 
    /* 
     * tty изначально был аппаратным устройством, который (обычно) 
     * ограничивался стандартом ASCII, в котором перевод строки
     * включал в себя два символа -- "возврат каретки" и "перевод строки".
     * В Unix, символ ASCII -- "перевод строки" заменял оба этих символа,
     * поэтому нам придется использовать для перевода строки
     * оба символа.
     *
     * Это одна из причин различий между текстовыми файлами Unix и
     * MS Windows. CP/M и ее "наследницы", например MS-DOS и
     * MS Windows, строго придерживались стандарта ASCII.
     */
    ((my_tty->driver)->write) (my_tty, 0, "\015\012", 2);
  }
}
 
static int __init print_string_init(void)
{
  print_string("The module has been inserted.  Hello world!");
  return 0;
}
 
static void __exit print_string_exit(void)
{
  print_string("The module has been removed.  Farewell world!");
}
 
module_init(print_string_init);
module_exit(print_string_exit);
 
 

9.2. Управление индикаторами на клавиатуре

При определенных условиях у вас может возникнуть желание дать вашему модулю более простой и более прямолинейный способ взаимодействия с внешним миром. Изменение состояния светодиодных индикаторов клавиатуры может быть одним из вариантов привлечения внимания пользователя или отображения некоторого состояния. Светодиодные индикаторы присутствуют на любой клавиатуре, они всегда находятся в поле зрения, они не нуждаются в установке, и их "подмаргивание" достаточно ненавязчиво, по сравнению с выводом на tty или в файл.

Следующий исходный код иллюстрирует модуль ядра, который после загрузки начинает мигать индикаторами клавиатуры.

Пример 9-2. kbleds.c

/* 
 *  kbleds.c - Мигание индикаторами на клавиатуре.
 */
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/tty.h>    /* определение fg_console, MAX_NR_CONSOLES */
#include <linux/kd.h>     /* определение KDSETLED */
#include <linux/console_struct.h> /* определение  vc_cons */
 
MODULE_DESCRIPTION("Пример module illustrating the use of Keyboard LEDs.");
MODULE_AUTHOR("Daniele Paolo Scarpazza");
MODULE_LICENSE("GPL");
 
struct timer_list my_timer;
struct tty_driver *my_driver;
char kbledstatus = 0;
 
#define BLINK_DELAY   HZ/5
#define ALL_LEDS_ON   0x07
#define RESTORE_LEDS  0xFF
 
/*
 * Функция my_timer_func мигает индикаторами на клавиатуре периодически вызывая 
 * ioctl() драйвера клавиатуры с командой KDSETLED. Дополнительную информацию,
 * по командам ioctl виртуального терминала, вы найдете в:
 *     /usr/src/linux/drivers/char/vt_ioctl.c, function vt_ioctl().
 *
 * Дополнительный аргумент команды KDSETLED -- значение 7 (перевод в режим
 * LED_SHOW_IOCTL -- управление индикаторами через ioctl), значение 0xFF --
 * (любое значение, большее 7, перевод в режим LED_SHOW_FLAGS --
 * отображение фактического состояния клавиатуры). Дополнительная информация:
 *     /usr/src/linux/drivers/char/keyboard.c, function setledstate().
 * 
 */
 
static void my_timer_func(unsigned long ptr)
{
  int *pstatus = (int *)ptr;
 
  if (*pstatus == ALL_LEDS_ON)
    *pstatus = RESTORE_LEDS;
  else
    *pstatus = ALL_LEDS_ON;
 
  (my_driver->ioctl) (vc_cons[fg_console].d->vc_tty, NULL, KDSETLED,
          *pstatus);
 
  my_timer.expires = jiffies + BLINK_DELAY;
  add_timer(&my_timer);
}
 
static int __init kbleds_init(void)
{
  int i;
 
  printk(KERN_INFO "kbleds: loading\n");
  printk(KERN_INFO "kbleds: fgconsole is %x\n", fg_console);
  for (i = 0; i < MAX_NR_CONSOLES; i++) {
    if (!vc_cons[i].d)
      break;
    printk(KERN_INFO "poet_atkm: console[geshifilter-questionmarkphp]i/%i] #%i, tty %lx\n&quot;, i,&#10;           MAX_NR_CONSOLES, vc_cons[i].d-&gt;vc_num,&#10;           (unsigned long)vc_cons[i].d-&gt;vc_tty);&#10;  }&#10;  printk(KERN_INFO &quot;kbleds: finished scanning consoles\n&quot;);&#10;&#10;  my_driver = vc_cons[fg_console].d-&gt;vc_tty-&gt;driver;&#10;  printk(KERN_INFO &quot;kbleds: tty driver magic %x\n&quot;, my_driver-&gt;magic);&#10;&#10;  /*&#10;   * Инициировать таймер&#10;   */&#10;  init_timer(&amp;my_timer);&#10;  my_timer.function = my_timer_func;&#10;  my_timer.data = (unsigned long)&amp;kbledstatus;&#10;  my_timer.expires = jiffies + BLINK_DELAY;&#10;  add_timer(&amp;my_timer);&#10;&#10;  return 0;&#10;}&#10;&#10;static void __exit kbleds_cleanup(void)&#10;{&#10;  printk(KERN_INFO &quot;kbleds: unloading...\n&quot;);&#10;  del_timer(&amp;my_timer);&#10;  (my_driver-&gt;ioctl) (vc_cons[fg_console].d-&gt;vc_tty, NULL, KDSETLED,&#10;          RESTORE_LEDS);&#10;}&#10;&#10;module_init(kbleds_init);&#10;module_exit(kbleds_cleanup);&#10;      &#10;      &#10;      &#10;
</div> <p>Если ни один из примеров данной главы вас не устраивает, можно попробовать другие хитрости, скрытые в ядре. Может быть вам подойдет опция CONFIG_LL_DEBUG в <strong class= "COMMAND">make menuconfig</strong>? Включив ее, вы получите низкоуровневый доступ к последовательному порту. Как бы страшно это ни прозвучало, но можете попробовать изменить реализацию <tt class="FILENAME">kernel/printk.c</tt> или какого нибудь другого системного вызова для вывода ascii-строк, чтобы иметь возможность отслеживать действия вашего модуля через последовательную линию связи.</p> <p>Несмотря на то, что вы уже встретили в этой книге намало наглядных приемов отладки, существует еще ряд моментов, которые вам необходимо знать. Отладка -- это всегда очень утомительный процесс и практически всегда он сопровождается внедрением значительного количества отладочного кода. Может сложиться так, что отладочный код не дает проявляться некоторым ошибкам. Поэтому, при выпуске вашего модуля, старайтесь свести отладочный код к минимуму и "прогнать" модуль еще раз, пытаясь обнаружить какие либо ошибки.</p> </div>[/geshifilter-questionmarkphp]