Чтение онлайн

на главную - закладки

Жанры

Разработка ядра Linux
Шрифт:

spin_unlock(&mr_lock);

В любой момент времени блокировка может удерживаться не более чем одним потоком выполнения. Следовательно, только одному потоку позволено войти в критический участок в данный момент времени. Это позволяет организовать защиту от состояний конкуренции на многопроцессорной машине. Заметим, что на однопроцессорной машине блокировки не компилируются в исполняемый код, и, соответственно, их просто не существует. Блокировки играют роль маркеров, чтобы запрещать и разрешать вытеснение кода (преемптивность) в режиме ядра. Если преемптивность ядра отключена, то блокировки совсем не компилируются.

Внимание: спин-блокировки не рекурсивны!

В отличие от реализаций в других операционных системах, спин-блокировки в операционной системе Linux не рекурсивны. Это означает, что если поток пытается захватить блокировку, которую он уже удерживает, то этот поток начнет периодическую проверку,

ожидая, пока он сам не освободит блокировку. Но поскольку поток будет периодически проверять, не освободилась ли блокировка, он никогда не сможет ее освободить, и возникнет тупиковая ситуация (самоблокировка). Нужно быть внимательными!

Спин-блокировки могут использоваться в обработчиках прерываний (семафоры не могут использоваться, поскольку они переводят процесс в состояние ожидания). Если блокировка используется в обработчике прерывания, то перед тем, как захватить эту блокировку (в другом месте — не в обработчике прерывания), необходимо запретить все локальные прерывания (запросы на прерывания на данном процессоре). В противном случае может возникнуть такая ситуация, что обработчик прерывания прерывает выполнение кода ядра, Который уже удерживает данную блокировку, и обработчик прерывания также пытается захватить эту же блокировку. Обработчик прерывания постоянно проверяет (spin), не освободилась ли блокировка. С другой стороны, код ядра, который удерживает блокировку, не будет выполняться, пока обработчик прерывания не закончит выполнение. Это пример взаимоблокировки (двойной захват), который обсуждался в предыдущей главе. Следует заметить, что прерывания необходимо запрещать только на текущем процессоре. Если прерывание возникает на другом процессоре (по отношению к коду ядра, захватившего блокировку) и обработчик будет ожидать на освобождение блокировки, то это не приведет к тому, что код ядра, который захватил блокировку, не сможет никогда ее освободить.

Ядро предоставляет интерфейс, который удобным способом позволяет запретить прерывания и захватить блокировку. Использовать его можно следующим образом.

spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;

unsigned long flags;

spin_lock_irqsave(&mr_lock, flags);

/* критический участок ... */

spin_unlock_irqrestore(&mr_lock, flags);

Подпрограмма

spin_lock_irqsave
сохраняет текущее состояние системы прерываний, запрещает прерывания и захватывает указанную блокировку. Функция
spin_unlock_irqrestore
, наоборот, освобождает указанную блокировку и восстанавливает предыдущее состояние системы прерываний. Таким образом, если прерывания были запрещены, показанный код не разрешит их по ошибке. Заметим, что переменная
flags
передается по значению. Это потому, что указанные функции частично выполнены в виде макросов.

На однопроцессорной машине показанный пример только лишь запретит прерывания, чтобы предотвратить доступ обработчика прерывания к совместно используемым данным, а механизм блокировок скомпилирован не будет. Функции захвата и освобождения блокировки также соответственно запрещают и разрешают преемптивность ядра.

Что необходимо блокировать

Важно, чтобы каждая блокировка была четко связана с тем, что она блокирует. Еще более важно — это защищать данные, а не код. Несмотря на то что во всех примерах этой главы рассматриваются критические участки, в основе этих критических участков лежат данные, которые требуют защиты, а никак не код. Если блокировки просто блокируют участки кода, то такой код труднопонимаем и подвержен состояниям гонок. Необходимо ассоциировать данные с соответствующими блокировками. Например, структура

struct foo
блокируется с помощью блокировки
foo_lock
. С данной блокировкой также необходимо ассоциировать некоторые данные. Если к некоторым данным осуществляется доступ, то необходимо гарантировать, что этот доступ будет безопасным. Наиболее часто это означает, что перед тем, как осуществить манипуляции с данными, необходимо захватить соответствующую блокировку и освободить эту блокировку нужно после завершения манипуляций.

Если точно известно, что прерывания разрешены, то нет необходимости восстанавливать предыдущее состояние системы прерываний. Можно просто разрешить прерывания при освобождении блокировки. В этом случае оптимальным будет использование функций

spin_lock_irq
и
spin_unlock_irq
.

spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;

spin_lock_irq(&mr_lock) ;

/* критический участок ... */

spin_unlock_irq(&mr_lock);

Для любого участка кода очень

сложно гарантировать, что прерывания всегда разрешены. В связи с этим не рекомендуется использовать функцию
spinlock_irq
. Если стоит вопрос об использовании этих функций, то лучше быть точно уверенным, что прерывания запрещены, а не огорчаться, когда найдете, что прерывания разрешены не там, где нужно.

Отладка спин-блокировок

Параметр конфигурации ядра

CONFIG_DEBUG_SPINLOCK
включает несколько отладочных проверок в коде спин-блокировок. Например, с этим параметром код спин-блокировок будет проверять использование неинициализированных спин-блокировок и освобождение блокировок, которые не были захваченными. При тестировании кода всегда необходимо включать отладку спин-блокировок.

Другие средства работы со спин-блокировками

Функция

spin_lock_init
используется для инициализации спин-блокировок, которые были созданы динамически (переменная типа
spinlock_t
, к которой нет прямого доступа, а есть только указатель на нее).

Функция

spin_try_lock
производит попытку захватить указанную спин-блокировку. Если блокировка находится в состоянии конфликта, то, вместо циклической проверки и ожидания на освобождение блокировки, эта функция возвращает ненулевое значение. Если блокировка была захвачена успешно, то функция возвращает нуль. Аналогично функция
spin_is_locked
возвращает ненулевое значение, если блокировка в данный момент захвачена. В противном случае возвращается нуль. Эта функция никогда не захватывает блокировку [48] .

48

Использование этих функций может привести к тому, что код становится "грязным". Нет необходимости часто проверять значение спин-блокировок — код или всегда должен захватывать блокировку, или вызываться только, если блокировка захвачена. Однако существуют некоторые ситуации, когда такие функции логично использовать, поэтому эти интерфейсы и предоставляются.

В табл. 9.3 приведен полный список функций работы со спин-блокировками.

Таблица 9.3. Список функций работы со спин-блокировками

Функция Описание
spin_lock
Захватить указанную блокировку
spin_lock_irq
Запретить прерывания на локальном процессоре и захватить указанную блокировку
spin_lock_irqsave
Сохранить текущее состояние системы прерываний, запретить прерывания на локальном процессоре и захватить указанную блокировку
spin_unlock
Освободить указанную блокировку
spin_unlock_irq
Освободить указанную блокировку и разрешить прерывания на локальном процессоре
spin_unlock_irqrestore
Освободить указанную блокировку и восстановить состояние системы прерываний на локальном процессоре в указанное первоначальное значение
spin_lock_init
Инициализировать объект типа
spinlock_t
в заданной области памяти
spin_trylock
Выполнить попытку захвата указанной блокировки и в случае неудачи возвратить ненулевое значение
spin_is_locked
Возвратить ненулевое значение, если указанная блокировка в данный момент захвачена, и нулевое значение в противном случае

Спин-блокировки и обработчики нижних половин

Как было указано в главе 7, "Обработка нижних половин и отложенные действия", при использовании блокировок в работе с обработчиками нижних половин необходимо принимать некоторые меры предосторожности. Функция

spin_lock_bh
позволяет захватить указанную блокировку и запретить все обработчики нижних половин. Функция
spin_unlock_bh
выполняет обратные действия.

Обработчик нижних половин может вытеснять код, который выполняется в контексте процесса, поэтому, если данные совместно используются обработчиком нижней половины и контекстом процесса, в контексте процесса эти данные необходимо защищать путем запрещения обработки нижних половин и захвата блокировки. Аналогично, поскольку обработчик прерывания может вытеснить обработчик нижней половины, необходимо запрещать прерывания и захватывать блокировку.

Поделиться:
Популярные книги

Барон нарушает правила

Ренгач Евгений
3. Закон сильного
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Барон нарушает правила

Помещица Бедная Лиза

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Помещица Бедная Лиза

Бальмануг. Студентка

Лашина Полина
2. Мир Десяти
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Бальмануг. Студентка

Изменить нельзя простить

Томченко Анна
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Изменить нельзя простить

Газлайтер. Том 3

Володин Григорий
3. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 3

Жандарм 4

Семин Никита
4. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Жандарм 4

Кодекс Охотника. Книга X

Винокуров Юрий
10. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
6.25
рейтинг книги
Кодекс Охотника. Книга X

Отмороженный 6.0

Гарцевич Евгений Александрович
6. Отмороженный
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Отмороженный 6.0

Пипец Котенку!

Майерс Александр
1. РОС: Пипец Котенку!
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Пипец Котенку!

АН (цикл 11 книг)

Тарс Элиан
Аномальный наследник
Фантастика:
фэнтези
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
АН (цикл 11 книг)

Толян и его команда

Иванов Дмитрий
6. Девяностые
Фантастика:
попаданцы
альтернативная история
7.17
рейтинг книги
Толян и его команда

Пятое правило дворянина

Герда Александр
5. Истинный дворянин
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Пятое правило дворянина

Игрок, забравшийся на вершину. Том 8

Михалек Дмитрий Владимирович
8. Игрок, забравшийся на вершину
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Игрок, забравшийся на вершину. Том 8

Возвышение Меркурия. Книга 7

Кронос Александр
7. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 7