Какой лучший механизм блокировки ядра Linux для конкретного сценария

Мне нужно решить проблему блокировки для этого сценария:

  1. Многопроцессорная система.
  2. Все процессоры используют общий (программный) ресурс.
  3. Доступ только для чтения к ресурсу очень распространен. (Обработка входящих сетевых пакетов)
  4. Доступ для записи гораздо реже. (Только изменения конфигурации).

В настоящее время я использую механизм read_lock_bh, write_lock_bh (спин-блокировки). Проблема в том, что чем больше процессоров, тем больше я получаю программных зависаний в контексте записи.

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

Итак, вопросы:

  1. Отдает ли механизм спин-блокировки Linux приоритет читателю/писателю/ни одному из них?
  2. Есть ли лучший механизм, который я могу использовать, чтобы избежать этих программных блокировок в моем сценарии, или, может быть, способ отдать приоритет писателю всякий раз, когда он пытается получить блокировку, используя мое текущее решение?

Спасибо, Нир


person Nir    schedule 31.05.2009    source источник
comment
Я не знаю ответа на этот вопрос, но в книге Understanding the Linux Kernel есть много полезной информации по этому типу вещей. Это действительно замечательная книга, ее должен прочитать каждый, кто работает с ядром.   -  person Zifre    schedule 31.05.2009
comment
Не очень хорошо разбираюсь в внутренностях параллелизма ядра, но вы можете создать свой собственный с подсчетом чтения/записи и блокировками ожидания записи.   -  person Aiden Bell    schedule 31.05.2009
comment
@Nir Рад видеть, что ты наконец принял ответ через 4 года :-)   -  person Robert S. Barnes    schedule 23.04.2013
comment
@Robert Hehe Да, хорошо, я просмотрел свой список вопросов, и в этом случае, хотя я на самом деле не сделал этого, потому что мне было лень, он на самом деле описывает лучший способ достичь того, что я просил. Так что лучше поздно, чем никогда :)   -  person Nir    schedule 24.04.2013
comment
@Nir תגיד, אתה ישראלי?   -  person Robert S. Barnes    schedule 28.06.2013


Ответы (3)


Вот прямая цитата из 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

Разве это не тот случай использования, для которого предназначен RCU? См. http://lwn.net/Articles/262464/, где можно найти хорошее описание его использования. .

person stsquad    schedule 01.06.2009
comment
Я читал о RCU в книге, которую я упомянул. Проблема в том, что, как я понимаю, он рассчитан только на одного писателя. Я могу больше. Кроме того, я думаю, что мне придется изменить свою архитектуру, чтобы она работала. Спасибо хоть. (Обновите все, а затем просто замените указатель.) - person Nir; 04.06.2009

Если работа, которую вы выполняете, удерживая блокировку, невелика, вы можете попробовать обычный мьютекс, без чтения-записи. Это более эффективно.

person Bruno Martinez    schedule 31.05.2009
comment
Это как бы упускает смысл наличия многопроцессорной машины. Это сделает его таким же полезным, как машина с одним процессором. - person Nir; 01.06.2009