unsigned long flags;
local_irq_save(flags);
/* прерывания запрещены . . */
local_irq_restore(flags);
/* состояние системы прерываний восстановлено
в первоначальное значение ... */
Нужно заметить, что эти функции являются макросами, поэтому передача параметра flags
выглядит как передача по значению. Этот параметр содержит зависящие от аппаратной платформы данные, которые в свою очередь содержат состояние системы прерываний. Так как, по крайней мере для одной аппаратной платформы (SPARC), в этой переменной хранится информация о стеке, то параметр flags
нельзя передавать в другие функции (другими словами, он должен оставаться в одном и том же стековом фрейме). По этой причине вызовы функций сохранения и восстановления должны выполняться в теле одной функции.
Все описанные функции могут вызываться как из обработчика прерываний, так и из контекста процесса.
Больше нет глобального вызова cli()
Ранее ядро предоставляло функцию, с помощью которой можно было запретить прерывания на всех процессорах системы. Более того, если какой-либо процессор вызывал эту функцию, то он должен был ждать, пока прерывания не будут разрешены. Эта функция называлась cli()
, а соответствующая ей разрешающая функция — sti()
; очень "x86-центрично" (хотя и было доступно для всех аппаратных платформ). Эти интерфейсы были изъяты во время разработки ядер серии 2.5, и, следовательно, все операции по синхронизации обработки прерываний должны использовать комбинацию функций по управлению локальными прерываниями и функций работы со спин-блокировками (обсуждаются в главе 9, "Средства синхронизации в ядре"). Это означает, что код, который ранее должен был всего лишь глобально запретить прерывания для того, чтобы получить монопольный доступ к совместно используемым данным, теперь должен выполнить несколько больше работы.
Ранее разработчики драйверов могли считать, что если в их обработчике прерывания и в любом другом коде, который имеет доступ к совместно используемым данным, вызывается функция cli()
, то это позволяет получить монопольный доступ. Функция cli()
позволяла гарантировать, что ни один из обработчиков прерываний (в том числе и другие экземпляры текущего обработчика) не выполняется. Более того, если любой другой процессор входит в участок кода, защищенный с помощью функции cli()
, то он не продолжит работу, пока первый процессор не выйдет из участка кода, защищенного с помощью функции cli()
, т.е. не вызовет функцию sti()
.
Изъятие глобальной функции cli()
имеет несколько преимуществ. Во-первых, это подталкивает разработчиков драйверов к использованию настоящих блокировок. Специальные блокировки на уровне мелких структурных единиц работают быстрее, чем глобальные блокировки, к которым относится и функция cli()
. Во-вторых, это упрощает значительную часть кода и позволяет удалить большой объем кода. В результате система обработки прерываний стала проще и понятнее.
Запрещение определенной линии прерывания
В предыдущем разделе были рассмотрены функции, которые позволяют запретить доставку всех прерываний на определенном процессоре. В некоторых случаях полезным может оказаться запрещение определенной линии прерывания во всей системе. Это называется маскированием линии прерывания. Например, может потребоваться запрещение доставки прерывания от некоторого устройства перед манипуляциями с его состоянием. Для этой цели операционная система Linux предоставляет четыре интерфейса.
void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
void synchronize_irq(unsigned int irq);
Первые две функции позволяют запретить указанную линию прерывания в контроллере прерываний. Это запрещает доставку данного прерывания всем процессорам в системе. Кроме того, функция disable_irq()
не возвращается до тех пор, пока все обработчики прерываний, которые в данный момент выполняются, не закончат работу. Таким образом гарантируется не только то, что прерывания с данной линии не будут доставляться, но и то, что все выполняющиеся обработчики закончили работу. Функция disable_irq_nosync()
не имеет последнего свойства.
Функция synchronize_irq()
будет ожидать, пока не завершится указанный обработчик, если он, конечно, выполняется.
Вызовы этих функций должны быть сгруппированы, т.е. каждому вызову функции disable_irq()
или disable_irq_nosync()
должен соответствовать вызов функции enable_irq()
. Только после последнего вызова функции enable_irq()
линия запроса на прерывание будет снова разрешена. Например, если функция disable_irq()
последовательно вызвана два раза, то линия запроса на прерывание не будет разрешена, пока функция enable_irq()
тоже не будет вызвана два раза.
Читать дальше