Взятие основных прерываний
Большинство драйверов устройств никогда не должно брать основные прерывания, потому что их линии прерывания не располагаются каскадом в контроллер прерываний системы. Например, FireWire и USB-устройства имеют понятие прерываний, но являются действительно просто сообщениями на последовательной шине. Они обычно упоминаются как программные прерывания, потому что кроме прерывания, вызванного самим сообщением, прерывание полностью моделируется в программном обеспечении.
Устройства PCI, однако, поддерживаются аппаратными прерываниями. Физическая линия работает между разъемом PCI и контроллером PCI, направляющим ту строку к вводу прерывания на контроллере прерываний, представляющем значение той строки как немного значения в регистре и повышающем линию прерывания процессора.
Можно взять аппаратное прерывание двумя способами: непосредственно через обработчика прерываний (основное прерывание) и косвенно через поток службы прерывания (вторичное прерывание). Второй метод строго предпочтен, поскольку он избегает генерировать чрезмерную задержку прерывания для других устройств в системе. Однако первый метод может использоваться для гарантии низкой работы задержки при необходимости.
Прерывания и дерево устройств
Откройте Firmware автоматически присваивает прерывания устройствам PCI. Обычно устройство PCI имеет только единственное прерывание, но возможно иметь устройства PCI, генерирующие больше чем одно прерывание (например, прерывание устройства и прерывание DMA).
В любом случае все прерывания, используемые устройством, перечислены в массиве как часть Ключа реестра I/O устройства. Как массив C, первое прерывание при смещении 0, втором при смещении 1, и т.д.
При регистрации для получения прерывания необходимо указать смещение в список прерываний для прерывания, которое Вы хотите получить. Если необходимо получить уведомление для многократных прерываний, необходимо включить им каждого индивидуально.
В маловероятном случае, что Открывают, Firmware не должен выделять прерывания правильно для Вашего устройства, необходимо связаться с Технической поддержкой Разработчика Apple для помощи, поскольку исправление проблем с выделением прерывания выходит за рамки этой книги.
Что такое Задержка прерывания?
Задержка прерывания относится на сумму времени между тем, когда прерывание инициировано и когда прерывание замечено программным обеспечением. Это может быть вызвано многими факторами. Некоторые из них включают:
другие драйверы, сохраняющие прерывания, отключены в течение длительного периода
блокирование в потоке службы прерывания
планирование задержки (ожидающий службы прерывания распараллеливают, чтобы быть запланированным),
служба прерывания распараллеливает приоритет (особенно для недрайверов ядра)
Если Вы пишете драйверы устройств, это - Ваша ответственность избежать вызывать слишком много задержки для других устройств в системе. В крайних случаях возможно фактически заставить прерывания быть отброшенными путем оставления прерываний выключенными слишком долго. Это может вызвать неустойчивость системы, и в некоторых машинах, может фактически заставить компьютер спонтанно выключаться путем порождения отказа синхронизации PMU.
Лучший способ избежать таких проблем состоит в том, чтобы использовать потоки службы прерывания. Это - предпочтительный способ обслужить прерывания. Однако, если Ваш драйвер хорошего поведения и не проводит огромное количество времени, копирующее данные, также возможно записать разумные драйверы устройств, работающие непосредственно в контексте прерывания. Это кратко обсуждено во Взятии Прерываний Непосредственно.
Прерывания через Потоки Службы Прерывания
Потоки службы прерывания являются стандартным способом обработать прерывания в драйверах устройств OS X, хотя они часто идут другим именем, циклом работы.
Цикл работы является потоком, единственная цель которого состоит в том, чтобы ожидать события, такого как прерывание для появления, затем вызовите надлежащую функцию-обработчик для выполнения фактической работы обработки прерывания — проверка результата, копирование данных, и т.д. Это действие происходит в потоке ядра.
Поток ядра не получает фактическое прерывание, как бы то ни было. Низкоуровневый обработчик прерываний получает основное прерывание. Это тогда генерирует программное прерывание, известное как вторичное прерывание. Поток службы прерывания получает то вторичное программное прерывание. Таким образом подпрограммы, работающие в потоке службы прерывания, не должны соблюдать те же правила как фактический обработчик прерываний; они могут блокировать, вызвать IOLog, и т.д.
Следующий код является примером регистрации для получения двух (вторичных) прерываний с помощью цикла работы:
/* class variables */ |
IOWorkLoop *myWorkLoop; |
IOFilterInterruptEventSource *interruptSource; |
IOFilterInterruptEventSource *DMAInterruptSource; |
/* code in start() */ |
myWorkLoop = IOWorkLoop::workLoop(); |
if( myWorkLoop == NULL) { |
IOLog( "org_mklinux_iokit_swim3_driver::start: Couldn't " |
allocate workloop event source\n"); |
return false; |
} |
interruptSource = IOFilterInterruptEventSource::filterInterruptEventSource( |
(OSObject*)this, |
(IOInterruptEventAction)&org_mklinux_iokit_swim3_driver::handleInterrupt, |
(Filter)&org_mklinux_iokit_swim3_driver::filterInterrupt, |
(IOService*)provider, |
(int)0 ); |
if ( interruptSource == NULL ) { |
IOLog( "org_mklinux_iokit_swim3_driver::start: Couldn't" |
"allocate Interrupt event source\n" ); |
return false; |
} |
if ( myWorkLoop->addEventSource( interruptSource ) != kIOReturnSuccess ) { |
IOLog( "org_mklinux_iokit_swim3_driver::start - Couldn't add Interrupt" |
"event source\n" ); return false;} |
DMAInterruptSource = IOFilterInterruptEventSource:: |
filterInterruptEventSource( |
(OSObject*)this, |
(IOInterruptEventAction)&org_mklinux_iokit_swim3_driver:: |
handleDMAInterrupt, |
(Filter)&org_mklinux_iokit_swim3_driver::filterDMAInterrupt |
(IOService*)provider, (int)1 ); |
if ( DMAInterruptSource == NULL ) { |
IOLog( "org_mklinux_iokit_swim3_driver::start: Couldn't" |
"allocate Interrupt event source\n" ); |
return false; |
} |
if ( myWorkLoop->addEventSource( DMAInterruptSource ) != kIOReturnSuccess ) |
{ |
IOLog( "org_mklinux_iokit_swim3_driver::start - Couldn't add " |
"Interrupt event source\n" ); |
return false; |
} |
myWorkLoop->enableAllInterrupts(); |
Методы handleInterrupt и handleDMAInterrupt будет теперь вызван для первых и вторых прерываний устройства (смещает 0 и 1), соответственно.
Заметьте методы filterInterrupt и filterDMAInterrupt. Эти методы вызывают по получении аппаратного прерывания и должны сделать минимальную сумму работы, необходимой, чтобы определить, принадлежит ли прерывание Вашему драйверу или нет.
Это использование фильтров прерывания строго рекомендуется IOInterruptEventSource гарантировать, что Ваш драйвер выполняет, а также возможный, должно Ваше устройство заканчивать тем, что совместно использовало прерывание с другим устройством. Если Вы не пишете функцию фильтра, система должна будет вызвать каждый драйвер, регистрирующийся для данного совместно использованного прерывания один за другим, приводя к неприятной задержке для всех включенных драйверов.
Если прерывание принадлежит Вашему устройству, метод фильтра должен возвратиться true. Иначе это должно возвратиться false. Поскольку два прерывания (устройство и DMA) выше, в теории, могли быть совместно использованы на той же линии прерывания, дополнительную заботу нужно соблюдать, чтобы определить природу прерывания и возвратиться true только для правильного вида прерывания — т.е. filterInterrupt должен возвратиться trueтолько для прерывания устройства, и filterDMAInterrupt должен возвратиться trueтолько для прерывания DMA.
Если возвращается Ваш фильтр прерывания true, Ваша подпрограмма обработчика прерываний будет запущена на Вашем цикле работы автоматически Набором I/O. Прерывание останется отключенным в аппаратных средствах, пока не завершится Ваша процедура обработки прерывания.
В некоторых случаях, такие как псевдо-DMA, это поведение может не быть тем, что Вы хотите. Поэтому можно выбрать иметь подпрограмму фильтра, планируют работу над самим циклом работы, затем возвращаются false. Если Вы сделаете это, то прерывание не будет отключено в аппаратных средствах, и таким образом, Вы могли получить дополнительные основные прерывания, прежде чем завершится Ваша служебная программа уровня цикла работы. Поскольку этот метод имеет импликации на синхронизации между Вашей подпрограммой фильтра и процедурой обработки прерывания, необходимо обычно избегать делать это, если драйвер не требует псевдо-DMA.
Для получения дополнительной информации консультируйтесь с документом Основные принципы IOKit и IOFilterInterruptEventSource документация класса в драйверах устройств ссылка API, доступная от веб-сайта Документации Разработчика Apple.
Взятие прерываний непосредственно
Этот метод обычно не желателен, поскольку он может вызвать нежелательное поведение включая высокую задержку прерывания для других драйверов, заикания аудио, потерял прерывания и даже ошибочное полное поведение системы.
Однако при необходимости в исключительно низкой задержке для определенных специализированных устройств может быть необходимо взять основные прерывания непосредственно. Необходимо помнить, что драйверы, берущие основные прерывания, не должны блокировать в их подпрограммах обработчика, что означает, что много вызовов не позволяются, включая IOLog.
Можно зарегистрировать обработчик для необработанного прерывания одним из двух способов: использование IOFilterInterruptEventSource или при помощи registerInterrupt.
При совместном использовании прерывания между многократными устройствами необходимо использовать IOFilterInterruptEventSource обработчик.
Если Вы пишете драйвер, обработчик прерываний которого должен работать частично в необработанном контексте прерывания, можно использовать registerInterrupt. Необходимо только рассмотреть выполнение обработки, которая чрезвычайно быстра в основном контексте прерывания. В целом, если объем работы сделанные взятия меньше времени, чем контекстное переключение, приемлемо сделать это в основном контексте прерывания.
Независимо, необходимо сделать только части низкой задержки в контексте прерывания и задержать любой тяжелый подъем к потоку службы прерывания.
Взятие основного прерывания Используя IOFilterInterruptEventSource
Для получения информации о IOFilterInterruptEventSource посмотрите Прерывания через Потоки Службы Прерывания.
Взятие Основного Прерывания Используя registerInterrupt
Взятие основного прерывания этим способом опасно. Вы никогда не должны делать этого, если Вы не решили, что невозможно получить соответствующую задержку с помощью вторичного обработчика прерываний. Перед продолжением необходимо связаться с Технической поддержкой Разработчика Apple для руководства.
При работе в основном контексте прерывания почти все вызовы небезопасны. Даже вещи как IOLog может блокировать, и блокирующий в основном контексте прерывания приведет к панике ядра (так как это иначе привело бы к необъясненному, зависают). Только простые блокировки Маха (спин-блокировки) безопасны в этом контексте, так как они исчезают в однопроцессорной среде.
Необходимо чрезвычайно стараться избежать сохранять прерывания выключенными в течение длительного периода времени. Например, опрос мультимиллисекунды, задержки и копирование больших объемов данных не приемлемы в основном контексте прерывания. Такое действие должно всегда задерживаться к потоку службы прерывания.
С теми протестами в памяти, примером кода ниже шоу, как зарегистрировать обработчик для основного устройства (аппаратные средства) прерывание непосредственно, не используя циклы работы. Первый пример показывает, как зарегистрировать функцию членства класса C++. Второй пример показывает, как зарегистрировать обычную функцию C.
provider->registerInterrupt(0, this, |
OSMemberFunctionCast(IOInterruptAction, this, |
&MyClass::handleInterrupt), 0); |
provider->enableInterrupt(0); |
provider->registerInterrupt(1, this, |
(IOInterruptAction) &handleDMAInterrupt, 0); |
provider->enableInterrupt(1); |
Как с предыдущим примером, когда первое прерывание (смещает 0) происходит, функция handleInterrupt будет вызван, и когда второе прерывание (смещает 1) происходит, функция handleDMAInterrupt будет вызван.