Какой лучший механизм блокировки ядра Linux для конкретного сценария
Мне нужно решить проблему блокировки для этого сценария:
- Многопроцессорная система.
- Все процессоры используют общий (программный) ресурс.
- Доступ только для чтения к ресурсу очень распространен. (Обработка входящих сетевых пакетов)
- Доступ на запись намного реже. (Довольно много изменений конфигурации только).
В настоящее время я использую read_lock_bh
, write_lock_bh
(спинлок) механизм. Проблема в том, что чем больше процессоров, тем больше у меня программных блокировок в контексте писателя.
Я прочитал главу о параллелизме в этой книге, но не мог понять, получит ли читатель или писатель приоритет при использовании спин-блокировок.
Итак, вопросы:
- Механизм спин-блокировки Linux отдает приоритет читателю / писателю / ни одному из них?
- Есть ли лучший механизм, который я мог бы использовать, чтобы избежать этих мягких блокировок в моем сценарии, или, может быть, для меня был бы способ отдать приоритет автору записи, когда он пытается получить блокировку, используя мое текущее решение?
Спасибо, Нир
3 ответа
Вот прямая цитата из драйверов устройств Essential Linux, которая может быть тем, что вы ищете. Похоже, что часть, касающаяся 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 от одновременного доступа. Потоки, которые должны искать таблицу маршрутизации для пересылки блокировок запросов на чтение пакетов. Потоки, которым нужно добавить или удалить записи из таблицы маршрутизации, получают блокировки записи. Это повышает производительность, потому что, как правило, случаев поиска таблиц маршрутизации гораздо больше, чем обновлений таблиц маршрутизации.
Как и обычные спин-блокировки, блокировки чтения-записи также имеют соответствующие варианты irq, а именно: read_lock_irqsave()
,read_lock_irqrestore()
, write_lock_irqsave()
, а также write_lock_irqrestore()
, Семантика этих функций аналогична таковой для регулярных спинлок.
Блокировки последовательностей или последовательные блокировки, введенные в ядре 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.
Разве это не тот случай использования, на который рассчитан RCU? См. http://lwn.net/Articles/262464/ для хорошего описания его использования.
Если работа, которую вы выполняете, удерживая блокировку, невелика, вы можете попробовать обычный мьютекс, не считывающий и не пишущий. Это более эффективно.