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


Читающие тракты реализуют критическую область следующим образом:

unsigned int seq; do {seq = read_seqbegin(&seqlock);
/ . CRITICAL REGION . /} while (read_seqretry(&seqlock, seq));
Функция read seqbegino возвращает текущий порядковый номер seqlock- блокировки. Функция read seqretry о возвращает , если либо значение локальной переменной seq нечетно (пишущий тракт обновлял данные, когда была вызвана функция read seqbegino), либо значение seq не совпадает с текущим значением счетчика порядковых номеров (пишущий тракт приступил к работе, когда читающий тракт все еще выполнял код критической области).
Обратите внимание, что когда читающий тракт входит в критическую область, он не должен отключать вытеснение в ядре. Зато пишущий тракт автоматически отключает вытеснение в ядре при входе в критическую область, поскольку он захватывает спин-блокировку.

Не каждую структуру данных можно защищать seqlock-блокировкой. В качестве общего правила можно сформулировать следующие условия:

- защищаемая структура не должна включать в себя указатели, которые модифицируются пишущими трактами и разыменуются читающими (в противном случае какой-нибудь пишущий тракт сможет изменить указатель под носом” у читающих трактов);
- код в критических областях читающих трактов не должен иметь побочных эффектов (в противном случае многократное чтение даст не тот результат, что однократное).
Кроме того, критические области читающих трактов должны быть короткими, а пишущие тракты должны редко получать seqlock-блокировку. В противном случае многократные попытки чтения потребуют непомерных накладных расходов. Типичным применением seqlock-блокировок в Linux является защита структур данных, связанных с хронометрированием

Обновление копии для чтения (RCU)

Обновление копии для чтения (Read-сору update, RCU) является еще одним приемом синхронизации, разработанным для защиты структур, к которым несколько процессоров обращается, в основном, для чтения. RCU позволяет нескольким пишущим и читающим трактам работать одновременно (это шаг вперед, по сравнению с seqlock-блокировками, позволяющими работать только одному пишущему тракту). Более того, техника обновления копии для чтения свободна от блокировок, т. е. в ней нет блокировки или счетчика, которые бы совместно использовались всеми процессорами. Это огромное преимущество перед спин-блокировками чтения/записи и seqlock-блокировками, которые требуют больших накладных расходов из-за снупинга и порчи строк кэша.
Каким образом применение техники RCU позволяет достичь удивительных результатов при синхронизации нескольких процессов без совместно используемых структур данных?

Основная идея состоит в ограничении области действия RCU:

- С помощью RCU можно защищать только динамически выделенные данные, доступные через указатели.
- Никакой управляющий тракт ядра не может спать” внутри критической области, защищенной с помощью RCU.
Когда управляющему тракту ядра необходимо прочитать структуру, защищенную С ПОМОЩЬЮ RCU, ОН выполняет макрос rcu_read_lock , эквивалентный макросу preempt disabie о. Затем читающий тракт разыменовывает указатель на структуру и приступает к ее чтению. Как было сказано, читающий тракт не может спать, пока не закончит читать структуру, а конец критической области отмечен макросом rcu read uniock , эквивалентным макросу preempt_enable.
Поскольку читающий тракт почти ничего не делает для предотвращения конфликтов одновременного обращения, мы вправе ожидать, что вся работа перекладывается на пишущий тракт. Действительно, когда пишущему тракту нужно обновить структуру, он разыменовывает указатель и делает копию всей структуры. Затем он обновляет копию. Закончив эту операцию, пишущий тракт изменяет указатель на структуру так, чтобы он ссылался на обновленную копию. Поскольку изменение значения указателя является атомарным действием, любой читающий или пишущий тракт видит либо старый экземпляр структуры, либо новый, и никакая порча данных невозможна. Однако при таком подходе необходим барьер памяти для гарантии того, что обновленный указатель будет виден на других процессорах только после модификации структуры данных. Такой барьер памяти неявно устанавливается, если в комбинации с обновлением копии используется спин-блокировка, предотвращающая параллельное выполнение пишущих трактов.

Реальная проблема с техникой обновления копии для чтения заключается в том, что старая копия не может быть освобождена сразу после того, как пишущий тракт изменит указатель. На практике читающие тракты, работавшие со структурой, когда пишущий тракт начал процедуру обновления, могут все еще читать старую копию. Ее можно освободить только после того, как все (потенциальные) читающие потоки на всех процессорах выполнят макрос rcu read uniock . Ядро требует, чтобы каждый потенциальный читающий тракт выполнил этот макрос до того, как:
- процессор выполнит переключение процессов;
- процессор начнет выполнение процесса в режиме пользователя;
- процессор перейдет к выполнению холостого цикла .В каждом из этих случаев мы говорим, что процессор перешел в состояние покоя.
Пишущий тракт вызывает функцию caiircuo, чтобы избавиться от старой копии структуры данных. Функция принимает в качестве параметров адрес дескриптора rcu head (который обычно встроен в освобождаемую структуру) и адрес функции обратного вызова, которая должна быть вызвана, когда все процессоры перейдут в состояние покоя. Эта функция обратного вызова, как правило, освобождает старую копию структуры данных.
Функция caii rcuo сохраняет в дескрипторе rcu head адрес функции обратного вызова и ее параметр, а затем заносит дескриптор в список функций обратного вызова (отдельный у каждого процессора). Периодически каждый тик таймера ядро проверяет, перешел ли процессор в состояние покоя. Когда все процессоры перейдут в состояние покоя, локальный тасклет, дескриптор которого хранится в процессорной переменной rcu taskiet, выполняет все функции обратного вызова, перечисленные в списке.

Обновление копии для чтения является нововведением в Linux ; оно используется в сетевом слое и в виртуальной файловой системе.

Предыдущая страница | 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...