Главная страница
Навигация по странице:

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

  • Таблица 9.3.

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

  • Спин-блокировки чтения-записи

  • Таблица 9.4.

  • Второе издание


    Скачать 3.09 Mb.
    НазваниеВторое издание
    Дата08.09.2019
    Размер3.09 Mb.
    Формат файлаpdf
    Имя файлаLav_Robert_Razrabotka_yadra_Linux_Litmir.net_264560_original_254.pdf
    ТипДокументы
    #86226
    страница25 из 53
    1   ...   21   22   23   24   25   26   27   28   ...   53
    Отладка спин-блокировок
    Параметр конфигурации ядра C O N F I G _ D E B U G _ S P I N L O C K включает несколько отладочных про- верок в коде спин-блокировок. Например, с этим параметром код спин-блокировок будет про- верять использование неинициализированных спин-блокировок и освобождение блокировок,
    которые не были захваченными. При тестировании кода всегда необходимо включать отладку спин-блокировок.
    Другие средства работы со спин-блокировками
    Функция spin_lock_init () используется для инициализации спин-блокировок,
    которые были созданы динамически (переменная типа spinlock_t, к которой нет прямого доступа, а есть только указатель на нее).
    Функция spin_try_lock () производит попытку захватить указанную спин-блоки- ровку. Если блокировка находится в состоянии конфликта, то, вместо циклической проверки и ожидания на освобождение блокировки, эта функция возвращает нену- левое значение. Если блокировка была захвачена успешно, то функция возвращает нуль. Аналогично функция spin_is_locked () возвращает ненулевое значение, если
    186 Глава 9
    блокировка в данный момент захвачена. В противном случае возвращается нуль. Эта функция никогда не захватывает блокировку
    2
    В табл. 9.3 приведен полный список функций работы со спин-блокировками.
    Таблица 9.3. Список функций работы со спин-блокировками
    Функция Описание
    spin_iock() Захватить указанную блокировку s p i n _ l o c k _ i r q ( ) Запретить прерывания на локальном процессоре и захватить указанную блокировку spin_lock_irqsave() Сохранить текущее состояние системы прерываний, запретить прерывания на локальном процессоре и захватить указанную блокировку spin_unlock() Освободить указанную блокировку spin_unlock_irq() Освободить указанную блокировку и разрешить прерывания на локальном процессоре spin_unlock_irqrestore() Освободить указанную блокировку и восстановить состояние си- стемы прерываний на локальном процессоре в указанное перво- начальное значение s p i n _ l o c k _ i n i t ( ) Инициализировать объект типа s p i n l o c k _ t в заданной области памяти s p i n _ t r y l o c k ( ) Выполнить попытку захвата указанной блокировки и в случае не- удачи возвратить ненулевое значение spin_is_locked() Возвратить ненулевое значение, если указанная блокировка в данный момент захвачена, и нулевое значение в противном случае
    Спин-блокировки и обработчики нижних половин
    Как было указано в главе 7, "Обработка нижних половин и отложенные действия",
    при использовании блокировок в работе с обработчиками нижних половин необхо- димо принимать некоторые меры предосторожности. Функция spin_lock_bh() по- зволяет захватить указанную блокировку и запретить все обработчики нижних по- ловин. Функция spin_unlock_bh() выполняет обратные действия.
    Обработчик нижних половин может вытеснять код, который выполняется в кон- тексте процесса, поэтому, если данные совместно используются обработчиком ниж- ней половины и контекстом процесса, в контексте процесса эти данные необходи- мо защищать путем запрещения обработки нижних половин и захвата блокировки.
    Аналогично, поскольку обработчик прерывания может вытеснить обработчик ниж- ней половины, необходимо запрещать прерывания и захватывать блокировку.
    Вспомним, что два тасклета (tasklet) одного типа не могут выполняться парал- лельно. Поэтому нет необходимости защищать данные, которые используются толь- ко тасклетами одного типа.
    2
    Использование этих функций может привести к тому, что код становится "грязным". Нет необхо- димости часто проверять значение спин-блокировок - код или всегда должен захватывать блоки- ровку, или вызываться только, если блокировка захвачена. Однако существуют некоторые ситуа- ции, когда такие функции логично использовать, поэтому эти интерфейсы и предоставляются.
    Средства синхронизации в ядре
    187

    Если данные используются тасклетами разных типов, то необходимо использо- вать обычную спин-блокировку перед тем, как обращаться к таким данным в обра- ботчике нижней половины. В этом случае нет необходимости запрещать обработку нижних половин, так как тасклет никогда не вытесняет другой тасклет, выполняю- щийся на том же процессоре.
    В случае отложенных прерываний (softirq), независимо от того, это отложенные прерывания одного типа или разных, данные, совместно используемые обработ- чиками отложенных прерываний, необходимо защищать с помощью блокировки.
    Вспомним, что обработчики отложенных прерываний, даже одного типа, могут вы- полняться одновременно на разных процессорах системы. Обработчик отложенного прерывания никогда не вытесняет другие обработчики отложенных прерываний, ко- торые выполняются на одном процессоре с ним, поэтому запрещать обработку ниж- них половин в этом случае не нужно.
    Спин-блокировки чтения-записи
    Иногда в соответствии с целью использования блокировок их можпо разде- лить два типа — блокировки чтения (reader lock) и блокировки записи (writer lock).
    Рассмотрим некоторый список, который может обновляться и в котором может вы- полняться поиск. Когда список обновляется (в него осуществляется запись), ника- кой другой код не может параллельно осуществлять запись или чтение этого списка.
    Запись означает исключительный доступ. С другой стороны, если в списке выполня- ется поиск (чтение информации), важно только, чтобы никто другой не выполнял записи в список. Работа со списком заданий в системе (как обсуждалось в главе 3,
    "Управление процессами") аналогична только что описанной ситуации. Не удиви- тельно, что список заданий в системе защищен с помощью спин-блокировки чтения- записи (reader-writer spin lock).
    Если работа со структурой данных может быть четко разделена на этапы чте- ния/записи, как в только что рассмотренном случае, то имеет смысл использовать механизмы блокировок с аналогичной семантикой. Для таких ситуаций операцион- ная система Linux предоставляет спин-блокировки чтения-записи. Спин-блокировки чтения-записи обеспечивают два варианта блокировки. Один или больше потоков выполнения, которые одновременно выполняют операции считывания, могут удер- живать такую блокировку. Блокировка на запись, наоборот, может удерживаться в любой момент времени только одним потоком, осуществляющим запись, и никаких параллельных считываний не разрешается. Блокировки чтения-записи иногда также называются соответственно shared/exclusive (общая/ исключающая) или concurrent/
    exclusive (параллельная/исключающая).
    Инициализировать блокировку для чтения-записи можно с помощью следующего программного кода.
    rwlock_t mr_rwlock = RW_LOCK_UNLOCKED;
    Следующий код осуществляет считывание.
    read_lock(&mr_rwlock);
    /* критический участок (только для считывания) ... */
    read unlock(&mr_rwlock);
    188 Глава 9

    И наконец, показанный ниже код осуществляет запись.
    write_lock(&mr_rwlock);
    /* критический участок (чтение и запись) ... */
    write_unlock{&mr_rwlock);
    Обычно считывание и запись информации осуществляются в разных участках кода, как это показано в данном примере.
    Заметим, что блокировку, захваченную для чтения, нельзя "повышать" до блоки- ровки, захваченной для записи. В следующем коде read_lock(&mr_rwlock);
    write_lock(&mr_rwlock);
    возникнет самоблокировка, так как при захвате блокировки на запись будет выпол- няться периодическая проверка, пока все потоки, которые захватили блокировку для чтения, ее не освободят; это касается и текущего потока. Если в каком-либо месте бу- дет необходима запись, то нужно сразу же захватывать блокировку для записи. Если в вашем коде нет четкого разделения на код, который осуществляет считывание, и код,
    который осуществляет запись, то нет необходимости использовать блокировки чтения- записи. В таком случае оптимальным будет использование обычных спин-блокировок.
    Несколько потоков чтения безопасно могут удерживать одну и ту же блокировку чтения-записи. На самом деле один поток также может безопасно рекурсивно захва- тывать одну и ту же блокировку для чтения. Это позволяет выполнить полезную и часто используемую оптимизацию. Если в обработчиках прерываний осуществляет- ся только чтение и не выполняется запись, то можно "смешивать" использование блокировок с запрещением прерываний и без запрещения. Для защиты данных при чтении можно использовать функцию r e a d _ l o c k () вместо r e a d _ l o c k _ i r q s a v e ().
    При обращении к данным для записи все равно необходимо запрещать прерывания,
    например использовать функцию w r i t e _ l o c k _ i r q s a v e ( ) , так как в обработчике прерывания может возникнуть взаимоблокировка в связи с ожиданием захвата бло- кировки на чтение при захваченной блокировке на запись. В табл. 9.4 показан пол- ный список средств работы с блокировками чтения-записи.
    Еще один факт, который необходимо принимать во внимание при работе с бло- кировками чтения-записи в операционной системе Linux, — это то, что блокировка на чтение всегда имеет большее преимущество по сравнению с блокировкой на за- пись. Если блокировка захвачена на чтение и поток записи ожидает на ее освобож- дение, то все потоки, которые будут пытаться захватить блокировку на чтение, будут добиваться успеха. Поток записи, который периодически проверяет на освобожде- ние блокировки, не сможет захватить блокировку, пока все потоки чтения эту блоки- ровку не освободят. Поэтому большое количество потоков чтения будет приводить к "подвисанию" ожидающих потоков записи. Это важное обстоятельство всегда нужно помнить при разработке схемы блокировок.
    Спин-блокировки обеспечивают очень быстрые и простые блокировки.
    Выполнение постоянных проверок в цикле является оптимальным, когда блокиров- ки захватываются на очень короткое время и код не может переходить в состояние ожидания (например, в обработчиках прерываний). Если же период времени ожида- ния на освобождение блокировки может быть большим или имеется потенциальная возможность перехода в состояние ожидания при захваченной блокировке, то зада- ча может быть решена с помощью семафоров.
    Средства синхронизации в ядре 189

    Таблица 9.4. Список функций работы со спин-блокировками чтения-записи
    Функция
    Описание
    read_lock ()
    read_lock_irq()
    read_lock_irqsave()
    read_unlock()
    read_unlock_irq ()
    read_unlock_irqrestore ()
    write_lock()
    write_lock_irq()
    write_lock_irqsave ()
    write_unlock ()
    write_unlock_irq ()
    write_unlock_irqrestore()
    write_trylock()
    rw_lock_init()
    rw is locked ()
    Захватить указанную блокировку на чтение
    Запретить прерывания на локальном процессоре и захватить ука- занную блокировку на чтение
    Сохранить состояние системы прерываний на текущем процессо- ре, запретить прерывания на локальном процессоре и захватить указанную блокировку на чтение
    Освободить указанную блокировку, захваченную для чтения
    Освободить указанную блокировку, захваченную для чтения,
    и разрешить прерывания на локальном процессоре
    Освободить указанную блокировку, захваченную для чтения, и вос- становить состояние системы прерываний в указанное значение
    Захватить заданную блокировку на запись
    Запретить прерывания на локальном процессоре и захватить ука- занную блокировку на запись
    Сохранить состояние системы прерываний на текущем процессо- ре, запретить прерывания на локальном процессоре и захватить указанную блокировку на запись
    Освободить указанную блокировку, захваченную для записи
    Освободить указанную блокировку, захваченную для записи, и разрешить прерывания на локальном процессоре
    Освободить указанную блокировку, захваченную для записи, и вос- становить состояние системы прерываний в указанное значение
    Выполнить попытку захватить заданную блокировку на запись и в случае неудачи возвратить ненулевое значение
    Инициализировать объект типа rwlock_t в заданной области памяти
    Возвратить ненулевое значение, если указанная блокировка за- хвачена, иначе возвратить нуль
    Семафоры
    В операционной системе Linux семафоры (semaphore) — это блокировки, кото- рые переводят процессы в состояние ожидания. Когда задание пытается захватить семафор, который уже удерживается, семафор помещает это задание в очередь ожи- дания (wait queue) и переводит это задание в состояние ожидания (sleep). Когда про- цессы
    3
    , которые удерживают семафор, освобождают блокировку, одно из заданий очереди ожидания возвращается к выполнению и может захватить семафор.
    Давайте снова возвратимся к аналогии двери и ключа. Когда человек, который хочет открыть дверь, подходит к двери, он может захватить ключ и войти в комна- ту. Отличие состоит в том, что произойдет, если к двери подойдет другой человек.
    В этом случае он записывает свое имя в список на двери и ложится поспать.
    3
    Как будет показано дальше, несколько процессов могут при необходимости одновременно удержи- вать один семафор.
    190
    Глава 9

    Когда человек, который находился внутри комнаты, выходит из нее, он прове- ряет список на двери. Если в списке есть чье-либо имя, то он выбирает первое из имен списка, дает соответствующему человеку пинка, будит его и позволяет войти в комнату. Таким образом, ключ (т.е. семафор) позволяет только одному человеку (т.е.
    потоку) находиться в комнате (т.е. в критическом разделе) в один момент времени.
    Если комната занята, то, вместо того чтобы периодически проверять, человек за- писывает свое имя в список (т.е. в очередь ожидания) и засыпает (т.е. блокируется в очереди ожидания и переходит в приостановленное состояние), что позволяет про- цессору выполнять некоторый другой код. Такой режим работы позволяет лучше ис- пользовать процессор, чем в случае спин-блокировок, так как при этом не тратится процессорное время на выполнение периодических проверок в цикле. Тем не менее использование семафоров, по сравнению со спин-блокировками, связано со значи- тельно большими накладными расходами.
    Из такого поведения семафоров, связанного с переводом процессов в состояние ожидания, можно сделать следующие интересные заключения.
    • Так как задания, которые конфликтуют при захвате блокировки, переводятся в состояние ожидания и в этом состоянии ждут, пока блокировка не будет осво- бождена, семафоры хорошо подходят для блокировок, которые могут удержи- ваться в течение длительного времени.
    • С другой стороны, семафоры не оптимальны для блокировок, которые удер- живаются в течение очень короткого периода времени, так как накладные за- траты на перевод процессов в состояние ожидания могут "перевесить" время,
    в течение которого удерживается блокировка.
    • Так как поток выполнения во время конфликта при захвате блокировки нахо- дится в состоянии ожидания, то семафоры можно захватывать только в кон- тексте процесса. Контекст прерывания планировщиком не управляется.
    • При удержании семафора (хотя разработчик может и не очень хотеть этого)
    процесс может переходить в состояние ожидания. Это не может привести к тупиковой ситуации, когда другой процесс попытается захватить блокировку
    (он просто переходит в состояние ожидания, что в конце концов дает возмож- ность выполняться первому процессу).
    • При захвате семафора нельзя удерживать спин-блокировку, поскольку процесс может переходить в состояние ожидания, ожидая на освобождение семафора,
    а при удержании спин-блокировки в состояние ожидания переходить нельзя.
    Эти факты подчеркивают особенности использования семафоров по сравнению со спин-блокировками. В большинстве случаев выбор решения, использовать или не использовать семафоры, прост. Если код должен переходить в состояние ожида- ния, что очень часто возникает при необходимости синхронизации с пространством пользователя, семафоры — единственное решение. Использовать семафоры всегда проще, даже если в них нет строгой необходимости, так как они предоставляют гиб- кость, связанную с переходом процессов в состояние ожидания. Если же возникает необходимость выбора между семафорами и спид-блокировками, то решение должно основываться на времени удержания блокировки. В идеале, все блокировки необхо- димо удерживать, по возможности, в течение наиболее короткого времени. При ис- пользовании семафоров, однако, допустимы более длительные периоды удержания
    Средства синхронизации в ядре 191
    блокировок. В дополнение ко всему, семафоры не запрещают преемптивность ядра,
    и, следовательно, код, который удерживает семафор, может быть вытеснен. Это означает, что семафоры не оказывают вредного влияния на задержки (латентность)
    планировщика.
    Последняя полезная функция семафоров — это то, что они позволяют иметь лю- бое количество потоков, которые одновременно удерживают семафор. В то время как спин-блокировки позволяют удерживать блокировку только одному заданию в любой момент времени, количество заданий, которым разрешено одновременно удерживать семафор, может быть задано при декларации семафора. Это значение на- зывается счетчиком использования (usage count) или просто счетчиком (count). Наиболее часто встречается ситуация, когда разрешенное количество потоков, которые од- новременно могут удерживать семафор, равно одному, как и для спин-блокировок.
    В таком случае счетчик использования равен единице и семафоры называются бинар-
    ными семафорами (binary semaphore) (потому что он может удерживаться только одним заданием или совсем никем не удерживаться) или взаимоисключающими блокировками
    (mutex, мьютекс) (потому что он гарантирует взаимоисключающий доступ —mutual ex- clusion). Кроме того, счетчику при инициализации может быть присвоено значение,
    большее единицы. В этом случае семафор называется счетным семафором (counting
    semaphore, семафор-счетчик), и он допускает количество потоков, которые одновре- менно удерживают блокировку, не большее чем значение счетчика использования.
    Семафоры-счетчики не используются для обеспечения взаимоисключающего досту- па, так как они позволяют нескольким потокам выполнения одновременно находить- ся в критическом участке. Вместо этого они используются для установки лимитов в определенном коде. В ядре они используются мало. Если вы используете семафор,
    то, скорее всего, вы используете взаимоисключающую блокировку (семафор со счет- чиком, равным единице).
    Семафоры были формализованы Эдсгером Вайбом Дейкстрой
    4
    (Edsger Wybe
    Dijkstra) в 1968 году как обобщенный механизм блокировок. Семафор поддерживает две атомарные операции Р () и V ( ) , название которых происходит от голландских слов Proben (тестировать) и Verhogen (выполнить инкремент). Позже эти операции на- чали называть down () и up () соответственно.
    В операционной системе Linux они имеют такое же название. Операция down ()
    используется для того, чтобы захватить семафор путем уменьшения его счетчика на единицу. Если значение этого счетчика больше или равно нулю, то блокировка за- хватывается успешно и задание может входить в критический участок. Если значе- ние счетчика меньше нуля, то задание помещается в очередь ожидания и процес- сор переходит к выполнению каких-либо других операций. Об использовании этой функции говорят в форме глагола— семафор опускается (down) для того, чтобы его захватить. Метод up () используется для того, чтобы освободить семафор после за- вершения выполнения критического участка. Эту операцию называют поднятием
    (upping) семафора.
    4
    Доктор Дейкстра (1930-2002 г.) один из самых талантливых ученых за всю (конечно, не очень дол- гую) историю существования вычислительной техники как области науки. Его многочисленные труды включают работы но проектированию операционных систем и по теории алгоритмов, сюда же входит концепция семафоров. Он родился в городе Роттердам, Нидерланды, и преподавал в университете штата Техас в течение 15 лет. Тем не менее, он был бы не очень доволен большим количеством директив GOTO в ядре Linux.
    i
    192 Глава 9

    Последний метод используется для инкремента значения счетчика. Если очередь ожидания семафора не пуста, то одно из заданий этой очереди возвращается к вы- полнению и захватывает семафор.
    1   ...   21   22   23   24   25   26   27   28   ...   53


    написать администратору сайта