Диспетчер прерываний 3ОС |
Одним из важных аспектов, связанных с эксплуатацией различных устройств в ОС, является
своевременная и оперативная обработка аппаратных прерываний. Учитывая широкий спектр
разработчиков устройств, практически не возможно выработать унифицированный механизм
поведения ОС при получении того или иного аппаратного прерывания (в дальнейшем IRQ).
Кроме того, разработчики устройств в условиях конкуренции, пытаются обойти соперника,
применяя закрытые, внутрикорпоративные стандарты и API, все это еще больше осложняет
процесс стандартизации и унификации устройств.
Таким образом, говоря об уникальности устройства, так же приходится говорить и
об уникальности кода его обработки, следствием из всего этого является понятия драйвера
устройства, инкапсулирующего в себе обслуживание аппаратных ресурсов устройства,
например, таких как прерывание или циклы шины ПДП. Являясь в терминах ядра ОС закрытым
модулем с ограниченным числом входов и выходов, драйвер, как правило, подчиняется
ядру в выборе политики планирования прерывания.
Вторым по важности кандидатом в плане обслуживания устройств выступает платформа, к которой физически подключаются аппаратура устройств, расширяющих функциональность платформы. В частности платформа 0x86 нормирует количество линий IRQ, ограничивая их количество 16. Являясь "кабальным" условием для работы, подключаемой к платформе 0x86 аппаратуры, оно предоставляет только единственный выход из подобного положения параллельного подключения устройств на канал IRQ. Сгруппированные на один физический канал IRQ устройства пользуются им последовательно, обозначив признак принадлежности прерывания физическому устройству через запись в адресное пространство портов В/В или памяти метки идентификатора. Естественно, что точный адрес местоположения и содержимое идентификационной метки известно только драйверу, управляющему работой устройства. Недопущение группировки на один физический канал, так же возможно при работе с устройствами монопольно использующими каналы IRQ. Ниже будут рассмотрены две эти схемы работы с устройствами реализуемые ядром 3OS.
Диспетчер прерываний ядра 3ОС
Диспетчер прерываний обеспечивает две схемы работы с драйверами устройств:
При управлении группой устройств, занимающих один физический канал IRQ, ядро должно выступать в роли арбитра, используя схему работы, реализующую стратегию разделяемой (SHARED) передачи управления драйверам устройств в функцию обработки прерывания.
Примечание: В дальнейших реализациях IPlanner код такой функции будет размещаться в непрерывном буфере IPlanner и автоматически завершаться условным переходом (по результатам проверки) на обработчик в драйвер. Такая стратегия позволит избавиться от инвалидации кэша команд и штрафа основного конвейера и конвейера предсказаний, сведя потери времени при идентификации прерывания и передачи управления в обработчик к минимуму.
Идентификация драйвера EXCLUSIVE не производится.
Схема поиска и идентификации драйвера
Низкая производительность платформы, нахождение большого количества драйверов в группе обработки SHARED прерывания с номером N могут привести к рекурсивному входу в IStackClass::IHook обрабатывающего данное прерывание. Такая рекурсия может быть вполне закономерной, если предположить, например, последовательное срабатывание устройств, захвативших прерывание IRQ N, и во время обработки предыдущего запроса. Однако при определенных условиях (низкая производительность платформы, неисправность устройства) может привести к бесконечному вложению прерывания.
Учитывая тот факт, что IPlanner разрешает вложенные прерывания практически сразу же при попадании в метод IStackClass::Seek, то бесконечная вложенность при отсутствии ее контроля, приведет к краху ядра в системе 3OS. Ограничить число вложений можно только величиной стека IPlanner для определенного экземпляра класса IStackClass, с соответствующим контролем при превышении его глубины исключения общей защиты. Для упрощений идентификации приведшего к бесконечной рекурсии прерывания, необходимо выделение собственного сегмента стека для каждого экземпляра класса IStackClass, обрабатывающего SHARED-цепочку драйверов.
По приходу первого прерывания в SHARED-группу драйверов, обязательно будет прерван либо поток драйвера, либо поток приложения. И тот, и другой имеют уникальный TSS, для сохранения контекста потока. TSS приложений содержит поля SS0:ESP0, указывающие на стек, переключаемый во время смены контекста (при прерывании, например) для того сегмента кода (привилегии 0) в который передается управление, назовем этот стэк стеком А. Для TSS драйвера те же поля содержат значения стэка B. Если при прерывании текущим контекстом был контекст приложения, то при входе в обработчик SHARED IRQ в IStackClass::IHook будет переключен стек A, при входе (первом) в драйвер смены стека произведено не будет, хотя активным станет TSS драйвера.
Схема контроля рекурсивного входа в IStackClass::IHook
Пусть группа устройств DEV_S [1,...,M] разделяют прерывание IRQ_N, типа SHARED. Т. к. 3ОС является многозадачной, многоуровневой системой, то код всех первичных обработчиков аппаратных прерываний IStackClass::IHook[1,..,0x20] имеет численно самый низкий уровень доступа AccessLevel = 0 (кольцо ядра). Соответственно, приходящий от устройства DEV_S[i] запрос на прерывание IRQ_N, может застать систему в одной из следующих ситуаций:
В случаях 1, 2 выполняется первичный вход в диспетчер прерываний, который находится в состоянии ожидания получения прерывания (WAIT IRQ), происходящие при этом системные процессы могут быть сведены в следующий список:
В случаях 3, 5 выполняется повторный (итерационный) вход в диспетчер прерываний, находящийся в состоянии произошедшей ранее передачи прерывания EXCLUSIVE или SHARED драйверу (TRANSFERRED IRQ). Возможность этой ситуации в случае 3 определяется допустимостью в разработчиком драйвера типа EXCLUSIVE приема прерываний внутри драйвера. Но, тем не менее, рассмотрим происходящие при этом процессы:
В случае 3 выполняется вложенный (рекурсивный) вход в диспетчер прерываний, находящийся в состоянии произошедшей ранее передачи прерывания SHARED драйверу этой же группы устройств DEV_S (RECURSIVE TRANSFERRED IRQ). Правильная обработка подобной ситуации определяется глубиной вложений стека определенного для всех драйверов системы, обслуживающих устройства DEV_S, использующих прерывание IRQ_N в SHARED режиме:
Таким образом, из приведенных выше описаний можно сделать следующее заключение: прерывание потока ПРИЛОЖЕНИЯ или СЕРВИСА, не может содержать вложений самого себя поскольку IPlanner находится в стационарном состоянии ожидания (WAIT) и для рассмотрения процесса рекурсии прерывания данный случай не составляет ни какого интереса. Возврат в поток ПРИЛОЖЕНИЯ или СЕРВИСА из прерывания будет произведен инструкцией IRETD по паре CS:EIP, где CS - это селектор TSS потока. Так как флаги будут подняты со стека в EFLAGS, флаг NT станет равен 1, указывая на вложение задачи, возращение произойдет переключением в задачу с последующим автоматическим восстановлением контекста.
Вход в IPlanner, когда последний находится в состоянии TRANSFERRED IRQ является точкой отсчета с которой необходимо начать следить за стеком, стартовый указатель входа в который будет передан именно в этом прерывании при смене уровня привилегий из TSS прерванного драйвера. Рассмотрим на схеме работу стека IPlanner:
Стек IPlanner
Как хорошо видно из схемы, ситуация, когда, например, прерывание от таймера придет в IPlanner в режимах TRANSFERRED IRQ и RECURSIVE TRANSFERRED IRQ, то будет порвана целостность стека, учитывающего рекурсию. В этом случае, либо планирование потоков должно учитывать эту ситуацию и возвращать управление обратно в IPlanner, либо должна существовать политика планирования, учитывающая возврат в IPlanner и повторное прерывание потока ПРИЛОЖЕНИЯ или СЕРВИСА в этих режимах.
Если по схеме возврат из драйвера по CS:EIP произойдет при состоянии RECURSIVE TRANSFERRED IRQ из любого I-ого вложения, произойдет возврат с понижением уровня привилегий, и при выходе за инструкцию CALL FAR IHandlerFunc, стек уже будет переключен на TSS->SS0:ESP0 (TSS покидаемого драйвера). При последующем IRET произойдет подъем флагов, стека и указателя возврата, а так как флаг вложенности NT будет поднят, то IRET автоматически вызовет переключение задачи и загрузку контекста прерванного до этого драйвера, и так до тех пор, пока не произойдет завершающий выход в поток.