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

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

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

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

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

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

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

Спасибо, Nir

Ответы [ 3 ]

6 голосов
/ 09 ноября 2009

Вот прямая цитата из 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 от одновременного доступа. Потоки что нужно искать в таблице маршрутизации для пересылки блокировок чтения запросов на пакеты. Темы, которые нужно добавить или удалить записи из таблицы маршрутизации, получить блокировку записи. Это улучшает производительность, потому что обычно есть гораздо больше случаев поиска таблиц маршрутизации, чем обновлений таблиц маршрутизации.

Как и обычные спин-блокировки, блокировки чтения-записи также имеют соответствующие варианты irq, а именно read_lock_irqsave(), read_lock_irqrestore(), write_lock_irqsave() и write_lock_irqrestore(). Семантика этих функции аналогичны функциям обычных спин-блокировки.

Блокировки последовательностей или seqlocks, представленные в ядре 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) и фактические данные (хранится в блоках данных). Каждый раз, когда вы работаете с файлом, компоненты в пути к файлу анализируются, и соответствующие зубные камни получены. Дентрии хранятся в кэше в структуре данных, называемой dcache, для ускорить будущие операции. В любое время количество поисков dcache намного больше, чем обновлений dcache, поэтому ссылки на dcache защищены с помощью примитивов RCU.

3 голосов
/ 01 июня 2009

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

0 голосов
/ 01 июня 2009

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

...