Статически условная переменная может быть создана с помощью макроса
DECLARE_COMPLETION(mr_comp);
Динамически созданная условная переменная может быть инициализирована с помощью функции init_completion()
.
Задание, которое должно ожидать на условной переменной, вызывает функцию wait_for_completion()
. После того как наступило ожидаемое событие, вызов функции complete()
посылает сигнал заданию, которое ожидает на условной переменной, и это задание возвращается к выполнению. В табл. 9.7 приведены методы работы с условными переменными.
Таблица. 9.7. Методы работы с условными переменными
Метод | Описание |
---|---|
init_completion(struct completion*) | Инициализация динамически созданной условной переменной в заданной области памяти |
wait_for_completion(struct completion*) | Ожидание сигнала на указанной условной переменной |
complete(struct completion*) | Отправка сигнала всем ожидающим заданиям и возвращение их к выполнению |
Для примеров использования условных переменных смотрите файлы kernel/sched.c
и kernel/fork.с
. Наиболее часто используются условные переменные, которые создаются динамически, как часть структур данных. Код ядра, который ожидает на инициализацию структуры данных, вызывает функцию wait_for_completion()
. Когда инициализация закончена, ожидающие задания возвращаются к выполнению с помощью вызова функции complete()
.
BKL: Большая блокировка ядра
Добро пожаловать к "рыжему пасынку" ядра. Большая блокировка ядра (Big Kernel Lock, BKL) — это глобальная спин-блокировка, которая была создана специально для того, чтобы облегчить переход от первоначальной реализации SMP в операционной системе Linux к мелкоструктурным блокировкам. Блокировка BKL имеет следующие интересные свойства.
• Во время удержания BKL можно переходить в состояние ожидания. Блокировка автоматически освобождается, когда задание переходит в состояние ожидания, и снова захватывается, когда задание планируется на выполнение. Конечно, это не означает, что
• Блокировка BKL рекурсивна. Один процесс может захватывать эту блокировку несколько раз подряд, и это не приведет к самоблокировке, как в случае обычных спин-блокировок.
• Блокировка BKL может использоваться только в контексте процесса.
• Блокировка BKL — это от лукавого.
Рассмотренные свойства дали возможность упростить переход от ядер серии 2.0 к серии 2.2. Когда в ядро 2.0 была введена поддержка SMP, только одно задание могло выполняться в режиме ядра в любой момент времени (конечно, сейчас ядро распараллелено очень хорошо — пройден огромный путь). Целью создания ядра серии 2.2 было обеспечение возможности параллельного выполнения кода ядра на нескольких процессорах. Блокировка BKL была введена для того, чтобы упростить переход к мелкоструктурным блокировкам. В те времена она оказала большую помощь, а сегодня она приводит к ухудшению масштабируемости[51].
Использовать блокировку BKL не рекомендуется. На самом деле, новый код никогда не должен использовать BKL. Однако эта блокировка все еще достаточно интенсивно используется в некоторых частях ядра. Поэтому важно понимать особенности большой блокировки ядра и интерфейса к ней. Блокировка BKL ведет себя, как обычная спин-блокировка, за исключением тех особенностей, которые были рассмотрены выше. Функция lock_kernel()
позволяет захватить блокировку, а функция unlock_kernel()
— освободить блокировку. Каждый поток выполнения может рекурсивно захватывать эту блокировку, но после этого необходимо столько же раз вызвать функцию unlock_kernel()
. При последнем вызове функции освобождения блокировки блокировка будет освобождена. Функция kernel_locked()
возвращает ненулевое значение, если блокировка в данный момент захвачена, в противном случае возвращается нуль. Эти интерфейсы определены в файле
. Рассмотрим простой пример использования этой блокировки.
lock_kernel();
/*
* Критический раздел, который синхронизирован со всеми пользователями
* блокировки BKL...
* Заметим, что здесь можно безопасно переходить в состояние ожидания
* и блокировка будет прозрачным образом освобождаться.
* После перепланирования блокировка будет прозрачным образом снова