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

  • Освобождение обработчика прерывания

  • Написание обработчика прерывания

  • Реентерабельность и обработчики прерываний

  • Совместно используемые обработчики

  • 1 1 6 Глава 6

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


    Скачать 3.09 Mb.
    НазваниеВторое издание
    Дата08.09.2019
    Размер3.09 Mb.
    Формат файлаpdf
    Имя файлаLav_Robert_Razrabotka_yadra_Linux_Litmir.net_264560_original_254.pdf
    ТипДокументы
    #86226
    страница15 из 53
    1   ...   11   12   13   14   15   16   17   18   ...   53

    Регистрация обработчика прерывания
    Ответственность за обработчики прерываний лежит на драйверах устройств, ко- торые управляют определенным типом аппаратного обеспечения. С каждым устрой- ством связан драйвер, и если устройство использует прерывания (а большинство ис- пользует), то драйвер должен выполнить регистрацию обработчика прерывания.
    Драйвер может зарегистрировать обработчик прерывания для обработки задан- ной линии с помощью следующей функции.
    /* request_irq: выделить заданную линию прерывания */
    int request_irq(unsigned int irq,
    irqreturn_t (*handler)(int, void *, struct pt_regs *),
    unsigned long irqflags,
    const char * devname,
    void *dev_id);
    Первый параметр, irq, указывает назначаемый номер прерывания. Для некото- рых устройств, таких как, например, обычные устройства персонального компьюте- ра, таймер и клавиатура, это значение, как правило, жестко закреплено. Для боль- шинства других устройств это значение определяется путем проверки (probing) или другим динамическим способом.
    Второй параметр, handler, — это указатель на функцию обработчика прерыва- ния, которая обслуживает данное прерывание. Эта функция вызывается, когда в операционную систему приходит прерывание. Следует обратить внимание на специ- фический прототип функции-обработчика. Она принимает три параметра и возвра- щает значение типа i r q r e t u r n _ t . Ниже в этой главе мы более подробно обсудим эту функцию.
    Третий параметр, irqflags, может быть равным нулю или содержать битовую маску с одним или несколькими следующими флагами.
    112 Глава 6

    • SA_INTERRUPT. Этот флаг указывает, что данный обработчик прерывания — это
    быстрый обработчик прерывания. Исторически так сложилось, что операцион- ная система Linux различает быстрые и медленные обработчики прерываний.
    Предполагается, что быстрые обработчики выполняются быстро, но потенци- ально очень часто, поэтому поведение обработчика прерывания изменяется,
    чтобы обеспечить максимально возможную скорость выполнения. Сегодня су- ществует только одно отличие: при выполнении быстрого обработчика преры- ваний запрещаются все прерывания на локальном процессоре. Это позволяет быстрому обработчику завершится быстро, и другие прерывания никак этому не пометают. По умолчанию (если этот флаг не установлен) разрешены все прерывания, кроме тех, которые маскированы на всех процессорах и обработ- чики которых в данный момент выполняются. Для всех прерываний, кроме прерываний таймера, нет необходимости устанавливать этот флаг.
    • SA_SAMPLE_RANDOM. Этот флаг указывает, что прерывания, сгенерированные данным устройством, должны вносить вклад в пул энтропии ядра. Пул энтро- пии ядра обеспечивает генерацию истинно случайных чисел на основе различ- ных случайных событий. Если этот флаг указан, то моменты времени, когда приходят прерывания, будут введены в пул энтропии. Этот флаг нельзя уста- навливать, если устройство генерирует прерывания в предсказуемые моменты времени (как, например, системный таймер) или на устройство может повли- ять внешний злоумышленник (как, например, сетевое устройство). С другой стороны, большинство устройств генерируют прерывания в непредсказуемые моменты времени и поэтому являются хорошим источником энтропии. Для более подробного описания пула энтропии ядра см.. приложение Б, "Генератор случайных чисел ядра".
    • SA_SHIRQ. Этот флаг указывает, что номер прерывания может совместно ис- пользоваться несколькими обработчиками прерываний (shared). Каждый обра- ботчик, который регистрируется на одну и ту же линию прерывания, должен указывать этот флаг. В противном случае для каждой линии может существовать только один обработчик прерывания. Более подробная информация о совмест- но используемых обработчиках прерываний приведена в следующем разделе.
    Четвертый параметр, devname, — это ASCII-строка, которая описывает, какое устройство связано с прерыванием. Например, для прерывания клавиатуры персо- нального компьютера это значение равно "keyboard". Текстовые имена устройств применяются для взаимодействия с пользователями с помощью интерфейсов
    /proc/irq и /proc/interrupts, которые вскоре будут рассмотрены.
    Пятый параметр, d e v i d , в основном, применяется для совместно используемых линий запросов на прерывания. Когда обработчик прерывания освобождается (опи- сано ниже), параметр dev_id обеспечивает уникальный идентификатор (cookie), ко- торый позволяет удалять только необходимый обработчик линии прерывания. Без этого параметра было бы невозможно ядру определить, какой обработчик данной линии прерывания следует удалить. Если линия запроса на прерывание не является совместно используемой, то можно в качестве этого параметра указывать NULL, если же номер прерывания является совместно используемым, то необходимо указывать уникальный идентификатор (cookie) (если устройство не подключено к тине ISA,
    то, скорее всего, оно поддерживает совместно используемые номера прерываний).
    Прерывания и обработка прерываний 113

    Этот параметр также передается обработчику прерывания при каждом вызо- ве. Обычная практика — это передача указателя на структуру устройства (контекст устройства), так как этот параметр является уникальным, и, кроме того, в обработ- чике прерывания может быть полезным иметь указатель на эту структуру.
    В случае успеха функция r e q u e s t _ i r q () возвращает нуль. Возврат ненулевого значения указывает на то, что произошла ошибка и указанный обработчик преры- вания не был зарегистрирован. Наиболее часто встречающийся код ошибки — это значение -EBUSY, что указывает на то, что данная линия запроса на прерывание уже занята (и или при текущем вызове, или при первом вызове не был указан флаг
    SA_SHIRQ).
    Следует обратить внимание, что функция r e q u e s t _ i r q () может переходить в состояние ожидания (sleep) и, соответственно, не может вызываться из кон- текста прерывания, или в других ситуациях, когда код не может блокироваться.
    Распространенной ошибкой является мнение, что функцию request_irq() можно безопасно вызывать в случаях, когда нельзя переходить в состояние ожидания. Это происходит отчасти от того, что действительно сразу непонятно, почему функция request_irq() должна чего-то ожидать. Дело в том. что при регистрации проис- ходит добавление информации о линии прерывания в каталоге /proc/irq. Функция proc_mkdir () используется для создания новых элементов на файловой системе procfs. Эта функция вызывает функцию proc_create () для создания новых эле- ментов файловой системы p r o c f s , которая в свою очередь вызывает функцию kmalloc () для выделения памяти. Как будет показано в главе 11, "Управление памя- тью", функция kmalloc () может переходить в состояние ожидания. Вот так вот!
    Для регистрации линии прерывания и инсталляции обработчика в коде драйвера можно использовать следующий вызов.
    if (request_irq(irqn, my_interrupt, SA_SHIRQ, "my_device", dev)){
    printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);
    return -EIO;
    }
    В этом примере параметр irqn — это запрошенный номер линии запроса на пре- рывание, параметр my_interrupt — это обработчик этой линии прерывания, ли- ния запроса на прерывание может быть совместно используемой, имя устройства —
    "my_device", dev — значение параметра dev_id. В случае ошибки код печатает сообщение, что произошла ошибка, и возвращается из выполняющейся функции.
    Если функция регистрации возвращает нулевое значение, то обработчик прерыва- ния инсталлирован успешно. С этого момента обработчик прерывания будет вы- зываться в ответ на приходящие прерывания. Важно произвести инициализацию оборудования и регистрацию обработчика прерывания в правильной последователь- ности, чтобы предотвратить возможность вызова обработчика до того момента,
    пока оборудование не инициализировано.
    Освобождение обработчика прерывания
    Для освобождения линии прерывания необходимо вызвать функцию void free_irq(unsigned int irq, void *dev_id)
    114 Глава 6

    Если указанная линия не является совместно используемой, то эта функция удаля- ет обработчик и запрещает линию прерывания. Если линия запроса на прерывание является совместно используемой, то удаляется обработчик, соответствующий па- раметру dev_id. Линия запроса на прерывание также запрещается, когда удаляется последний обработчик. Теперь понятно, почему важно передавать уникальное зна- чение параметра dev_id. При использовании совместно используемых прерываний требуется уникальный идентификатор для того, чтобы отличать друг от друга раз- личные обработчики, связанные с одним номером прерывания, и позволить функ- ции free_irq() удалять правильный обработчик. В любом случае, если параметр devoid не равен значению NULL, то он должен соответствовать тому обработчику,
    который удаляется.
    Вызов функции free_irq() должен производиться из контекста процесса.
    Таблица 6 . 1 . Список функций управления регистрацией прерываний
    Функция Описание r e q u e s t _ i r q () Зарегистрировать заданный обработчик прерывания для заданной линии прерывания f r e e _ i r q () Освободить указанный обработчик прерывания. Если с линией прерывания больше не связан ни один обработчик, то запретить указанную линию пре- рывания
    Написание обработчика прерывания
    Следующее описание является типичным для обработчика прерывания.
    static irqreturn_t intr_handler (int irq, void *dev_id, struct pt_regs *regs)
    Заметим, что оно должно соответствовать аргументу, который передается в функ- цию request_irq (). Первый параметр, irq, — это численное значение номера пре- рывания, которое обслуживается обработчиком. Сейчас этот параметр практически не используется, кроме разве что при печати сообщений. Для версий ядра, меньших
    2.0, не было параметра dev_id, поэтому параметр i r q использовался, чтобы разли- чать устройства, которые обслуживаются одним драйвером, и поэтому используют один и тот же обработчик прерываний (как пример можно рассмотреть компьютер с несколькими контроллерами жесткого диска одного типа).
    Второй параметр, dev_id, — это указатель, равный значению, которое было пе- редано в функцию request_irq () при регистрации обработчика прерывания. Если значение этого параметра является уникальным, что необходимо для поддержки со- вместно используемых прерываний, то его можно использовать как идентификатор для того, чтобы отличать друг от друга различные устройства, которые потенциаль- но могут использовать один обработчик. В связи с тем, что структура (контекст)
    устройства (device structure) является как уникальной, так и, возможно, полезной при использовании в обработчике, обычно в качестве параметра dev_id передают указатель на эту структуру.
    Последний параметр, regs, — это указатель на структуру, содержащую значения регистров процессора и состояние процессора, которые были сохранены перед на- чалом обслуживания прерывания. Этот параметр используется редко, в основном
    . . _
    Прерывания и обработка прерываний 115
    для отладки. Сейчас разработчики начинают склоняться к мысли, что этот параметр нужно убрать. В существующих обработчиках прерываний он используется мало, и если его убрать, то не будет больших разочарований.
    Возвращаемое значение обработчиков прерываний имеет специальный тип irqreturn_t. Обработчик может возвращать два специальных значения: IRQ_NONE
    или IRQ_HANDLED. Первое значение возвращается, если обработчик прерывания обнаружил, что устройство, которое он обслуживает, не является источником пре- рывания. Второе значение возвращается, если обработчик вызван правильно и устройство, которое он обслуживает, является источником прерывания. Кроме это- го, может быть использован макрос IRQ_RETVAL (x). Если значение параметра х не равно нулю, то макрос возвращает значение IRQ_HANDLED, иначе возвращается зна- чение, равное IRQ_NONE. Эти специальные значения позволяют дать ядру информа- цию о том, генерирует ли устройство паразитные (необрабатываемые) прерывания.
    Если все обработчики прерывания, которые обслуживают данную линию, возвра- щают значение IRQ_NONE, то ядро может обнаружить проблему. Заметим, что этот странный тип возвращаемого значения, i r q r e t u r n _ t , просто соответствует типу int. Подстановка типа используется для того, чтобы обеспечить совместимость с бо- лее ранними версиями ядра, у которых не было подобной функции. До серии ядер
    2.6 обработчик прерывания имел возвращаемое значение типа void. В коде новых драйверов можно применить переопределение типа typedef i r q r e t u r n _ t в тип void и драйверы могут работать с ядрами серии 2.4 без дальнейшей модификации.
    Обработчик прерываний может помечаться как s t a t i c , так как он никогда не вызывается непосредственно в других файлах кода.
    Роль обработчика прерывания зависит только от устройства и тех причин, по ко- торым это устройство генерирует прерывания. Минимально, обработчик прерыва- ния должен отправить устройству подтверждение о том, что прерывание получено.
    Дли более сложных устройств необходимо дополнительно отправить и принять дан- ные, а также выполнить другую более сложную работу. Как уже упоминалось, слож- ная работа должна по возможности выполняться обработчиком нижней половины прерывания, которые будут рассмотрены в следующей главе.
    Реентерабельность и обработчики прерываний
    Обработчики прерываний в операционной системе Linux не обязаны быть реентерабельными.
    Когда выполняется некоторый обработчик прерывания, соответствующая линия запроса на пре- рывание маскируется на всех процессорах, что предотвращает возможность приема запроса на прерывание с этой пинии. Обычно все остальные прерывания в этот момент разрешены, поэто- му другие прерывания могут обслуживаться, тогда как текущая линия всегда является запре- щенной. Следовательно, никакой обработчик прерываний никогда не вызывается параллельно самому себе для обработки вложенных запросов на прерывание. Это позволяет значительно упростить написание обработчиков прерываний.
    Совместно используемые обработчики
    Совместно используемые (shared) обработчики выполняются практически так же,
    как и не совместно используемые. Существует, однако, три главных отличия.
    • Флат SA_SHIRQ должен быть установлен в параметре flags при вызове функ- ции request_irq ().
    1 1 6 Глава 6

    • Аргумент dev_id этой же функции должен быть уникальным для каждого за- регистрированного обработчика. Достаточным является передача указателя на структуру, которая описывает устройство. Обычно так и поступают, поскольку структура контекста устройства является уникальной для каждого устройства,
    и, кроме того, данные этой структуры потенциально могут быть полезными при выполнении обработчика. Для совместно используемого обработчика нельзя присваивать параметру dev_id значение NULL!
    • Обработчик прерывания должен иметь возможность распознать, сгенерирова- но ли прерывание тем устройством, которое обслуживается этим обработчи- ком. Для этого требуется как поддержка аппаратного обеспечения, так и на- личие соответствующей логики в обработчике прерывания. Если аппаратное устройство не имеет необходимых функций, то не будет никакой возможности в обработчике прерывания определить, какое из устройств на совместно ис- пользуемой линии является источником прерывания.
    Все драйверы, которые рассчитаны на совместно используемую линию преры- вания, должны удовлетворять указанным выше требованиям. Если хотя бы одно из устройств, которые совместно используют линию прерывания, не делает это коррек- тно, то все остальные устройства также не смогут совместно использовать линию.
    Если функция request_irq () вызывается с указанием флага SH_SHIRQ, то этот вы- зов будет успешным только в том случае, если для данной линии прерывания еще нет зарегистрированных обработчиков или все обработчики для данной линии за- регистрированы с указанием флага SH_SHIRQ. Заметим, что для ядер серии 2.6, в отличие от более ранних серий, можно "смешивать" совместно используемые обра- ботчики с различными значениями флага SA_INTERRUPT.
    Когда ядро получает прерывание, то оно последовательно вызывает все обработ- чики, зарегистрированные для данной линии. Поэтому важно, чтобы обработчик прерывания был в состоянии определить, какое устройство является источником этого прерывания. Обработчик должен быстро завершиться, если соответствующее ему устройство не генерировало это прерывание. Такое условие требует, чтобы аппа- ратное устройство имело регистр состояния (status register) или другой аналогичный механизм, которым обработчик может воспользоваться для проверки. На самом деле большинство устройств действительно имеют данную функцию.
    Настоящий обработчик прерывания
    Давайте рассмотрим настоящий обработчик прерывания, который используется в драйвере устройства RTC (real-time clock, часы реального времени), находящегося в файле d r i v e r s / c h a r / r t c . с . Устройство RTC есть во многих вычислительных си- стемах, включая персональные компьютеры (PC). Это отдельное от системного тай- мера устройство, которое используется для установки системных часов, для подачи сигналов таймера (alarm) или для реализации генераторов периодических сигналов
    (periodic timer). Установка системных часов обычно производится путем записи зна- чений в специальный регистр или диапазон адресов (номеров портов) ввода-вывода
    (I/O range). Подача сигналов таймера или генератор периодических сигналов обыч- но реализуются через прерывания. Прерывание эквивалентно некоторому сигналу таймера: оно генерируется, когда истекает период времени сигнального таймера.
    При загрузке драйвера устройства RTC вызывается функция r t c i n i t () для ини-
    Прерывания и обработка прерываний 117
    циализации драйвера. Одна из ее обязанностей — это регистрация обработчика пре- рывания. Делается это следующим образом.
    if (request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL) {
    printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
    return -EIO;
    }
    Из данного примера видно, что номер линии прерывания — это константа
    RTC_IRQ, значение которой определяется отдельно для каждой аппаратной плат- формы с помощью препроцессора. Например, для персональных компьютеров это значение всегда соответствует IRQ 8. Второй параметр— это обработчик прерыва- ния, r t c i n t e r r u p t , при выполнении которого запрещены все прерывания в связи с указанием флага SA_INTERRUPT. Из четвертого параметра можно заключить, что драйвер будет иметь имя " r t c " . Так как наше устройство не может использовать ли- нию прерывания совместно с другими устройствами и обработчик прерывания не используется для каких-либо других целей, в качестве параметра dev_id передается значение NULL.
    И наконец, собственно сам обработчик прерывания.
    /*
    * Очень маленький обработчик прерывания. Он выполняется с
    * установленным флагом SA_INTERRUPT, однако существует
    * возможность конфликта с выполнением функции set_rtc_mmss ()
    * (обработчик прерывания rtc и обработчик прерывания системного
    * таймера могут выполняться одновременно на двух разных
    * процессорах). Следовательно, необходимо сериализировать доступ
    * к микросхеме с помощью спин-блокировки rtc_lock, что должно
    * быть сделано для всех аппаратных платформ в коде работы с
    * таймером. (Тело функции set_rtc_mmss() ищите в файлах
    * ./arch/XXXX/kernel/time.c)
    */
    static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    /*
    * Прерывание может оказаться прерыванием таймера, прерыванием
    * завершения обновления или периодическим прерыванием.
    * Состояние (причина) прерывания хранится в самом
    * младшем байте, а общее количество прерывание — в оставшейся
    * части переменной rtc_irq_data
    */
    spin_lock (&rtc_lock);
    rtc_irq_data += 0x100;
    rtc_irq_data &=

    Oxff;
    rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & OxFO);
    if (rtc_status & RTC_TIMER_ON)
    rnod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
    1   ...   11   12   13   14   15   16   17   18   ...   53


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