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

       

Down


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

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

У читателя может возникнуть вопрос: может ли произойти антипереполнение величины count? He может: процесс после уменьшения величины count всегда переходит в состояние ожидания, поэтому каждый конкретный процесс может захватить одновременно только один семафор, и в запасе есть еще намного больше отрицательных значений величины типа int по сравнению с числом процессов.

Если знаковый разряд установлен, семафор отрицателен. Это значит, что значение count было равным 0 или отрицательным непосредственно перед тем, как было уменьшено, поэтому процесс не сумел получить семафор и должен перейти в состояние ожидания до тех пор, пока семафор не станет доступным. Реализация этого потребовала применения в следующих нескольких строках многих хитростей. Команда js выполняет переход, если знаковый разряд установлен (то есть, если результат команды decl был отрицателен), и 2f обозначает цель перехода. Здесь 2f — не шестнадцатиричное значение, а специальный синтаксис ассемблера GNU: 2 означает переход к локальному символу «2», a f — поиск этого символа впереди. (2b означало бы поиск самого последнего локального символа «2» сзади.) Этот локальный символ находится в строке .

Ветвление не выполнено, поэтому процесс получил семафор. Это фактически конец функции down, несмотря на то, что он так не выглядит. Это вскоре станет ясно.

Одной из самых сложных для понимания частей функции down является директива .section, находящаяся непосредственно перед адресатом перехода, которая указывает, что следующий код нужно собрать в отдельной секции ядра: в секции под названием .text.lock. Данная секция будет размещаться в памяти и рассматриваться как выполнимая программа. Это указано флажком ах — строкой, которая следует за именем секции; обратите внимание, что этот флажок ах не имеет ничего общего с регистром АХ процессора х86. В результате ассемблер перемещает команды в строках и из секции down, в которой они находятся, в другую секцию выполнимого кода ядра, поэтому объектный код, вырабатываемый на основе этих строк, не является физически смежным с кодом, вырабатываемом на основе предыдущих строк. Вот почему строка представляет собой конец функции down.


Это адресат перехода, достигаемый, если нельзя было получить семафор. Команда pushl $1b (ее можно также записать и без знака $) не помещает в стек шестнадцатиричное значение 1b — это была бы команда pushl S0x1b. Вместо этого, данное значение 1b представляет собой тот же синтаксис ассемблера GNU, с которым мы ранее встретились в случае применения 2f: он указывает на адрес команды, в данном случае — на адрес первой локальной метки «1», которая встретится при поиске в обратном направлении. Таким образом, эта команда помещает в стек адрес строки ; данный адрес станет адресом возврата, чтобы после последующего перехода ход выполнения вернулся к концу функции down.

Переход отсюда к функции __down_failed (не включена в эту книгу). Эта функция сохраняет несколько регистров в стеке и вызывает функцию __down (строка ), которая будет описана ниже, для выполнения работы по ожиданию семафора. После возврата из функции __down функция __down_failed возвращается к down, которая также выполняет возврат. Функция __down не выполняет возврат до тех пор, пока процесс не приобретет семафор; в результате, когда бы ни произошел возврат из функции down, процесс имеет семафор, независимо от того, получил ли он его немедленно или должен был ждать.

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


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