Обработка прерываний (Linux/General)

На mainbord у нас есть контроллер прерываний (IRC), который действует как мультиплексор между устройствами, которые могут поднять прерывание и CPU:

                             |--------|
          |-----------|      |        |
-(0)------| IRC  _____|______| CPU    |
-(...)----| ____/     |      |        |
-(15)-----|/          |      |--------|
          |-----------|

Каждое устройство связано с IRQ (число слева). После каждого выполнения CPU определяет линию запроса прерывания. Если обнаружен сигнал, будет выполнено сохранение состояния, а ЦП загрузит процедуру обработчика прерываний, которая может быть найдена в векторе прерываний, который находится по фиксированному адресу в памяти. Насколько я могу видеть, число IRQ и номер вектора в векторе прерываний не совпадают, поскольку у меня есть, например, моя сетевая карта, зарегистрированная в IRQ 8. На процессоре Intel Pentium это будет указывать на рутину, которая используется чтобы сигнализировать одно условие ошибки, поэтому должно быть какое-то отображение, которое указывает на правильный обработчик.

Вопросы:

1) Если я напишу драйвер устройства и зарегистрирую для него IRQ X. Откуда система знает, какое устройство должно быть обработано? Я могу, например, использовать request_irq() с IRQ-номером 10, но как система знает, что обработчик должен использоваться для мыши или клавиатуры или для того, что я пишу драйвер?

2) Как выглядит вектор прерывания? Я имею в виду, что если я использую IRQ 10 для своего устройства, это перепишет стандартный обработчик, который предназначен для обработки ошибок в таблице (первая используемая версия - 32 согласно Silberschatz (понятия операционной системы)).

3) Кто инициализирует IRQ? Биос? ОС?

4) Кто отвечает за соответствие IRQ и смещение в векторе прерываний?

5) Можно разделить IRQS. Как это возможно? На материнской плате есть аппаратные дорожки, которые подключают устройства к контроллеру прерываний. Как можно настроить полосы для одного и того же прерывания? Должна быть таблица, в которой говорится, что дорожка 2 и 3 обрабатывает IRQ15, например. Где находится эта таблица и как она называется?

Ответы

Ответ 1

Ответы на ядро ​​Linux. Должен работать и для большинства других ОС.

1) Если я напишу драйвер устройства и зарегистрирую для него IRQ X. Откуда система знает, какое устройство должно быть обработано? Я могу, например, использовать request_irq() с IRQ-номером 10, но как система знает, что обработчик должен использоваться для мыши или клавиатуры или для того, что я пишу драйвер?

На него нет 1 ответа. Например, если это пользовательская встроенная система, разработчик оборудования скажет автору драйвера "Я собираюсь перенаправить устройство x на irq y". Для большей гибкости, например, для сетевой карты, которая обычно использует протокол PCI. Существует арбитраж уровня аппаратного обеспечения/прошивки для назначения номера irq новому устройству при его обнаружении. Затем это будет записано в один из регистров конфигурации PCI. Сначала драйвер считывает этот регистр устройства и затем регистрирует обработчик прерываний для этого конкретного irq. Аналогичные механизмы будут существовать и для других протоколов.

Что вы можете сделать, это искать вызовы request_irq в коде ядра и как драйвер получил значение irq. Он будет отличаться для каждого типа драйверов.

Таким образом, ответ на этот вопрос, система не знает. Разработчик аппаратного обеспечения или аппаратные протоколы предоставляют эту информацию автору драйверов. И затем писатель драйвера регистрирует обработчик для этого конкретного irq, сообщая системе, что делать, если вы видите, что irq.

2) Как выглядит вектор прерывания? Я имею в виду, что если я использую IRQ 10 для своего устройства, это перепишет стандартный обработчик, который предназначен для обработки ошибок в таблице (первая используемая версия - 32 согласно Silberschatz (понятия операционной системы)).

Хороший вопрос. Есть две части.

a) Когда вы request_irq (irq, обработчик). Система действительно не программирует запись 0 в IVT или IDT. Но запись N + irq. Где N - количество обработчиков ошибок или исключений общего назначения, поддерживаемых на этом CPU. Информация зависит от системы и системы.

b) Что произойдет, если вы ошибочно запросите irq, который используется другим драйвером. Вы получаете ошибку, и IDT не запрограммирован с вашим обработчиком.

Примечание. IDT - таблица дескрипторов прерываний.

3) Кто инициализирует IRQ? Биос? ОС?

Bios сначала, а затем ОС. Но есть некоторые ОС, например, MS-DOS, которая не перепрограммирует IVT, установленную BIOS. Более сложные современные ОС, такие как Windows или Linux, не хотят полагаться на определенные функции BIOS, и они перепрограммируют IDT. Но биос должен сделать это изначально только тогда, когда ОС вступает в картину.

4) Кто отвечает за соответствие IRQ и смещение в векторе прерываний?

Я действительно не понимаю, что вы имеете в виду. Поток такой. Сначала вашему устройству присваивается номер irq, а затем вы регистрируете обработчик для него с этим номером irq. Если вы используете неправильный номер irq, а затем включите прерывание на своем устройстве, система выйдет из строя. Поскольку обработчик зарегистрирован за неправильным номером irq.

5) Можно разделить IRQS. Как это возможно? На материнской плате есть аппаратные дорожки, которые подключают устройства к контроллеру прерываний. Как можно настроить полосы для одного и того же прерывания? Должна быть таблица, в которой говорится, что дорожка 2 и 3 обрабатывает IRQ15, например. Где находится эта таблица и как она называется?

Это очень хороший вопрос. Экстра-таблица не так, как она решена в ядре. Скорее для каждого общего irq, обработчики хранятся в связанном списке указателей функций. Ядро проходит через все обработчики и вызывает их один за другим, пока один из обработчиков не потребует прерывания как своего собственного.

The code looks like this:

driver1:

d1_int_handler:
       if (device_interrupted()) <------------- This reads the hardware
       {
           do_interrupt_handling();
           return MY_INTERRUPT;
       }else {
           return NOT_MY_INTERRUPT;
       }

driver2:
       Similar to driver 1


kernel:
      do_irq(irq n)
      {
           if (shared_irq(n))
           {
                irq_chain = get_chain(n);
                while(irq_chain)
                {
                    if ((ret = irq_chain->handler()) == MY_INTERRUPT) 
                        break;
                    irq_chain = irq_chain->next;
                }
                if (ret != MY_INTERRUPT)
                     error "None of the drivers accepted the interrupt";
           }
      }