Ядро Linux в комментариях

       

Run_bottom_halves


Теперь ядро готово выполнить ждущие обработки нижние половины.

Сохраняет текущий набор активных — т.е. помеченных — нижних половин в локальной переменной active и с помощью макроса clear_active_bhs (строка ) очищает установленные разряды в глобальной переменной bh_active. Очистка этих разрядов в переменной bh_active одновременно удаляет метки со всех нижних половин.

Теперь читатели могут убедиться, что иногда нижние половины объединяются в пакеты, как и утверждалось ранее без доказательств. К этому моменту прерывания включены, поэтому если другое прерывание запускается и отмечает уже помеченную нижнюю половину раньше, чем функция run_bottom_halves успеет скопировать bh_active в active, верхняя половина будет выполнена дважды, а нижняя — только один раз. И, поскольку само прерывание также может быть прервано, верхняя половина могла бы быть выполнена трижды при однократном выполнении нижней половины, и т.д. Однако вероятность этого быстро уменьшается с увеличением количества рекурсивных прерываний.

Может ли данный код пропустить какие-либо нижние половины? Давайте предположим, что прерывание происходит в самый неудачный момент времени: между строками и  — т.е. после копирования переменной bh_active но перед очисткой разрядов установки в ней. Возможны три случая:

  • Новое прерывание не помечает ни одной нижней половины. Совершенно очевидно, что этот случай не создает никаких проблем — набор нижних половин, подлежащих обслуживанию, после прерывания остается тем же, что и до него; таким образом функция run_bottom_halves выполняет все нижние половины, которые она должна была выполнить.
  • Новое прерывание помечает нижнюю половину, которая уже была помечена. В этом случае также не возникает никаких проблем — функция run_bottom_halves все равно собиралась выполнить эту нижнюю половину, и она делает это, когда выполняется возврат из нового прерывания.
  • Новое прерывание помечает нижнюю половину, метка с которой ранее была удалена. В этом случае переменная active больше не соответствует bh_active, когда функция run_bottom_halves выполняет цикл по нижним половинам. Однако, строка не очистит вновь установленный разряд в bh_active, поскольку функция clear_active_bhs очищает только те разряды, которые устанавливаются в active. Функция clear_active_bhs использует функцию atomic_clear_mask (строка ), которая выполняет просто поразрядное AND разрядов установленных в active и оставляет остальные разряды неизменными. Таким образом, когда функция выполняет цикл, она не станет обслуживать вновь помеченную нижнюю половину немедленно, но поскольку в bh_active ее разряд все же установлен, run_bottom_halves запомнит о необходимости обслужить ее со временем. Точнее говоря, любые нижние половины, которые пропускаются таким образом, будут обслужены во время обработки нижних половин, следующей за следующим прерыванием таймера, одним мигом позже — или даже раньше, если до этого произойдет какое-нибудь другое прерывание. Таким образом, «пропущенным» нижним половинам обычно не придется ожидать дольше, чем сотую доли секунды, а поскольку, по определению, нижние половины в любом случае не критичны в отношении времени, эта небольшая задержка не имеет никакого значения.



  • Одновременно просматривает массив bh_base и разряды в переменной active. Если самый младший разряд в active установлен, соответствующая нижняя половина вызывается; затем цикл переходит к следующей записи массива bh_base и к следующему разряду в active.

    Поскольку данный цикл является циклом do/while, он выполняется по меньшей мере один раз. Частично это связанно с тем, что вызывающая функция всегда, прежде чем вызывать функцию do_bottom_half, проверяет, нуждаются ли какие-либо нижние половины в обслуживании, и поэтому цикл должен быть выполнен по меньшей мере один раз. Кстати, та проверка могла бы быть помещена и внутрь самой функции do_bottom_half, но выполнение проверки прежде вызова функции предотвращает вызов, если никакие нижние половины не нуждаются в выполнении. В любом случае цикл будет выполняться правильно, даже если никакие нижние половины не нуждаются в выполнении; это привело бы к напрасной трате времени, но не принесло бы никакого иного вреда.

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


    Содержание раздела