Linux Kernel (Ядро линукса) (часть 1)


Запрет и разрешение функций отложенного выполнения

Следовательно, структуры, к которым обращаются функции отложенного выполнения, должны быть защищены от попыток одновременного обращения.
Тривиальным способом запрещения функций отложенного выполнения на процессоре является отключение на нем прерываний. Поскольку обработчики прерываний не выполняются, softirq-функции не могут быть вызваны асинхронно.

Однако, как мы увидим в следующем разделе, иногда ядру нужно запретить выполнение функций отложенного выполнения, не отключая прерывания. Локальные функции отложенного выполнения могут быть разрешены или запрещены на локальном процессоре путем записи соответствующего значе
ния в счетчик softirq-функций, который хранится в поле preempt count дескриптора thread info текущего процесса.

Вспомним, что функция do softirqO не станет выполнять softirq-функцию, если значение счетчика положительно. Более того, тасклеты реализованы на основе softirq-функций, и поэтому установка счетчика в положительное значение запрещает выполнение всех функций отложенного выполнения на данном процессоре, а не только softirq-функций.

Макрос locai bh disabie прибавляет единицу к счетчику softirq-функций локального процессора, а макрос locai bh enabie вычитает единицу из этого счетчика. Таким образом, ядро может сделать несколько вложенных вызовов макроса locai bh disabie. Выполнение функций отложенного выполнения будет разрешено снова только макросом locai bh enabie, соответствующим первому макросу locai bh disabie.

Уменьшив значение счетчика softirq-функций, макрос locai bh enabie выполняет две важные операции, обеспечивающие своевременное выполнение потоков, ожидающих своей очереди долгое время:

1. Проверяет счетчики аппаратных прерываний и softirq-функций в поле preempt count локального процессора. Если оба они равны нулю, и имеются softirq-функции, ожидающие выполнения, макрос вызывает функцию do softirqO для их активизации
2. Проверяет, установлен ли флаг tif need resched у локального процессора. Если установлен, значит, висит” запрос на переключение процессов. В этом случае макрос вызывает функцию preempt scheduie .Синхронизация обращений к структурам данных ядра

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

В свою очередь, уровень параллельности зависит от двух основных факторов:

- от количества устройств ввода/вывода, работающих одновременно;
- от количества процессоров, выполняющих производительную работу.
Для увеличения производительности ввода/вывода следует отключать прерывания лишь на короткие промежутки времени. когда прерывания отключены, программируемый контроллер прерываний временно игнорирует IRQ-запросы, выдаваемые устройствами ввода/вывода, и никакие операции с этими устройствами не могут начаться.Чтобы процессоры были использованы эффективно, следует, по возможности, избегать примитивов синхронизации, основанных на спин-блокировках. Когда процессор выполняет плотный” цикл инструкций в ожидании освобождения спин-блокировки, он впустую расходует драгоценные машинные циклы. Хуже того, как мы уже говорили, спин-блокировки оказывают негативное воздействие на производительность системы в целом из-за их влияния на аппаратные кэши.

Рассмотрим пару случаев, в которых можно достичь синхронизации, обеспечивая высокий уровень параллельности:

- совместно используемую структуру, состоящую из единственного целого значения, можно обновлять, если объявить ее с типом atomic t и применять к ней атомарные операции. Атомарная операция работает быстрее, чем спин-блокировки и отключение прерываний. Она замедляет только управляющие тракты ядра, одновременно обращающиеся к структуре;
- занесение элемента в совместно используемый связный список никогда не бывает атомарным действием, потому что включает в себя, как минимум, два обновления указателей. Тем не менее ядро иногда может выполнить вставку элемента без применения блокировок и отключения прерываний. В качестве примера, объясняющего, почему это работает, мы рассмотрим ситуацию, в которой служебная процедура системного вызова заносит новые элементы в однонаправленный список, а обработчик прерываний или функция отложенного выполнения асинхронно просматривает этот список.На языке С вставка элемента реализуется следующими операторами присваивания:
new->next = list_element->next; list_element->next = new;
На ассемблере вставка элемента сводится к двум атомарным инструкциям, идущим друг за другом. Первая устанавливает указатель next элемента new, но не модифицирует список. Таким образом, если обработчик прерываний просмотрит список между выполнением первой и второй инструкцией, он не увидит там нового элемента. Если же обработчик прерываний обратится к списку после выполнения второй инструкции, новый элемент уже будет в списке. Важно то, что в любом случае поддерживается непротиворечивость и целостность списка. Однако эти его свойства сохраняются, только если обработчик прерываний не модифицирует список. В противном случае указатель next, только что установленный у элемента new, может стать некорректным.
Разработчики не должны допустить, чтобы порядок следования этих двух операций присваивания был изменен компилятором или управляющим блоком процессора. В противном случае, если служебная процедура системного вызова будет прервана между двумя присваиваниями, обработчик прерываний обнаружит испорченный список.

Следовательно, нужно установить барьер записи в память:


new->next = list_element->next; wmb ;
list_element->next = new;
Выбор между спин-блокировками, семафорами и отключением прерываний. К сожалению, схемы обращения к большинству структур ядра гораздо сложнее простых примеров, рассмотренных в предыдущем разделе, и разработчикам ядра приходится применять семафоры, спин-блокировки, отмену прерываний и запрет soft irq-функций. Вообще говоря, выбор примитива синхронизации зависит от того, какие управляющие тракты ядра обращаются к структуре Не будем забывать, что, как только управляющий тракт ядра получает спин-блокировку (а также блокировку чтения/записи, seqlock- блокировку или блокировку чтения вида обновление копии для чтения”), отменяет локальные прерывания или запрещает локальные softirq-функции, автоматически отключается вытеснение в ядре.

Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница




Возможно, Вас также заинтересует:

ОС Knoppix - это Linux без про...

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

Linux Kernel (Ядро линукса) (ч...

Спин-блокировкаСпин-блокировка необходима в многопроцессорной системе, потому что могут возникнуть...

Linux Kernel (Ядро линукса) (ч...

Копирование при записи В системах Unix первых поколений создание процесса было реализовано довольно...

Linux Kernel (Ядро линукса) (ч...

Буферы блоков и головы буферовУ каждого буфера есть дескриптор голова буфера, имеющий тип buffer...