Вот прямая цитата из Essential Linux Device Drivers, которая может быть тем, что вы ищете. Кажется, что часть, посвященная RCU в конце, может быть тем, что вас интересует.
Блокировка чтения-записи
Другим специализированным механизмом регулирования параллелизма является вариант спин-блокировок чтения-записи. Если использование критической секции таково, что отдельные потоки либо читают, либо пишут в общую структуру данных, но не делают и то, и другое, эти блокировки являются естественным подходом. Внутри критической области одновременно допускается несколько потоков чтения. Спин-блокировки считывателя определяются следующим образом:
rwlock_t myrwlock = RW_LOCK_UNLOCKED;
read_lock(&myrwlock); /* Acquire reader lock */
/* ... Critical Region ... */
read_unlock(&myrwlock); /* Release lock */
Однако, если поток записи входит в критическую секцию, другие потоки чтения или записи не допускаются внутрь. Чтобы использовать спин-блокировки записи, вы должны написать это:
rwlock_t myrwlock = RW_LOCK_UNLOCKED;
write_lock(&myrwlock); /* Acquire writer lock */
/* ... Critical Region ... */
write_unlock(&myrwlock); /* Release lock */
Посмотрите на код маршрутизации IPX, представленный в net/ipx/ipx_route.c
, как реальный пример спин-блокировки чтения-записи. Блокировка чтения-записи под названием ipx_routes_lock
защищает таблицу маршрутизации IPX от одновременного доступа. Потоки, которым необходимо просматривать таблицу маршрутизации для пересылки пакетов, запрашивают блокировки считывателя. Потоки, которым необходимо добавить или удалить записи из таблицы маршрутизации, получают блокировки записи. Это повышает производительность, поскольку обычно выполняется гораздо больше операций поиска в таблице маршрутизации, чем обновлений таблицы маршрутизации.
Как и обычные спин-блокировки, блокировки чтения-записи также имеют соответствующие варианты прерывания, а именно read_lock_irqsave()
, read_lock_irqrestore()
, write_lock_irqsave()
и write_lock_irqrestore()
. Семантика этих функций аналогична семантике обычных спин-блокировок.
Блокировки последовательности или seqlock, представленные в ядре 2.6, представляют собой блокировки чтения-записи, при которых пишущие предпочтительнее читающих. Это полезно, если операций записи в переменную намного больше, чем операций чтения. Примером может служить переменная jiffies_64
, обсуждавшаяся ранее в этой главе. Потоки записи не ждут читателей, которые могут находиться внутри критической секции. Из-за этого потоки чтения могут обнаружить, что их вход в критический раздел не удался, и им может потребоваться повторная попытка:
u64 get_jiffies_64(void) /* Defined in kernel/time.c */
{
unsigned long seq;
u64 ret;
do {
seq = read_seqbegin(&xtime_lock);
ret = jiffies_64;
} while (read_seqretry(&xtime_lock, seq));
return ret;
}
Писатели защищают критические области с помощью write_seqlock()
и write_sequnlock()
.
В ядре 2.6 появился еще один механизм, называемый Read-Copy Update (RCU), который обеспечивает повышенную производительность, когда число читателей значительно превышает число записывающих. Основная идея заключается в том, что потоки чтения могут выполняться без блокировки. Потоки записи более сложны. Они выполняют операции обновления копии структуры данных и заменяют указатель, который видят читатели. Исходная копия сохраняется до следующего переключения контекста на всех процессорах, чтобы гарантировать завершение всех текущих операций чтения. Имейте в виду, что использование RCU более сложно, чем использование примитивов, обсуждавшихся до сих пор, и его следует использовать только в том случае, если вы уверены, что это правильный инструмент для работы. Структуры данных RCU и функции интерфейса определены в include/linux/rcupdate.h
. В Documentation/RCU/*
имеется достаточно документации.
Пример использования RCU см. на fs/dcache.c
. В Linux каждый файл связан с информацией о записи каталога (хранящейся в структуре, называемой dentry), информацией о метаданных (хранящейся в inode) и фактическими данными (хранящимися в блоках данных). Каждый раз, когда вы работаете с файлом, анализируются компоненты в пути к файлу и получаются соответствующие dentries. Дентри хранятся в кэше в структуре данных, называемой dcache, для ускорения будущих операций. В любое время количество обращений к dcache намного больше, чем количество обновлений dcache, поэтому ссылки на dcache защищены с помощью примитивов RCU.
person
Robert S. Barnes
schedule
09.11.2009