Блокировка файлов против семафоров - PullRequest
7 голосов
/ 25 августа 2010

Просто из любопытства, какой предпочтительный способ добиться межпроцессной синхронизации в Linux?Семейство системных вызовов sem*(2) имеет очень неуклюжий и устаревший интерфейс, в то время как есть три способа блокировки файлов - fcntl(), flock() и lockf().

Каковы внутренние различия(если есть) и как бы вы обосновали использование каждого из них?

Ответы [ 4 ]

8 голосов
/ 25 августа 2010

Ни. Фактические версии pthread_* (например, phtread_mutex_t) позволяют размещать переменные в общих сегментах, которые создаются с помощью shm_open. Вам просто нужно добавить дополнительный параметр к вызовам инициализации.

Не используйте семафоры (sem_t), если вам не нужно, они слишком низкого уровня и прерываются IO и т. Д.

Не злоупотребляйте блокировкой файлов для межпроцессного управления. Это не сделано для этого. В частности, у вас нет возможности сбросить метаданные файла, такие как блокировки, поэтому вы никогда не знаете, когда блокировка / разблокировка станет видимой для второго процесса.

4 голосов
/ 25 августа 2010

Как отмечает DarkDust, вы страдаете от богатого выбора. Для чего это стоит, мое дерево решений выглядит примерно так:

Используйте взаимные исключения, когда только один процесс / поток может иметь доступ одновременно.

Используйте семафоры, когда два или более (но, тем не менее, конечных) процесса / потока могут использовать ресурс.

Используйте семафоры POSIX, если вам действительно не нужно что-то, что есть у семафоров SYSV - например, UNDO, PID последней операции и т. Д.

Используйте блокировку файлов для файлов или, если вышеперечисленное каким-либо образом не соответствует вашим требованиям.

3 голосов
/ 25 августа 2010

Различные реализации блокировок / семафоров появились на разных системах.В System V Unix у вас было semget / semop, POSIX определил другую реализацию с sem_init, sem_wait и sem_post.И, насколько я понял, flock возникла в 4.2BSD.

Поскольку все они приобрели определенное значение, Linux теперь поддерживает их всех, чтобы упростить портирование.Кроме того, flock является мьютексом (заблокированным или разблокированным), но функции sem* (как SysV, так и POSIX) являются семафорами: они позволяют приложению предоставлять доступ нескольким параллельным процессам, например, вы можете разрешить доступ к ресурсу для4 процесса одновременно с семафорами.Вы можете реализовать мьютекс с семафорами, но не наоборот.Я помню, что в превосходном «Расширенном программировании UNIX» Марка Дж. Рохкинда он продемонстрировал, как передавать данные между процессами через семафоры (очень неэффективно, он сделал это только для того, чтобы доказать, что это возможно).Но я не смог найти ничего надежного в эффективности.

Я думаю, это больше похоже на «Используй то, что хочешь».

2 голосов
/ 26 августа 2010

Потенциально значимой разницей может быть справедливость распределения ресурсов.Я не знаю подробностей реализации семейства semget/semop, но я подозреваю, что он обычно реализуется как "традиционный" семафор в плане планирования.Как правило, я считаю, что выпущенные потоки обрабатываются на основе FIFO (первый, ожидающий семафор, выпускается первым).Я не думаю, что это произойдет с блокировкой файлов, так как я подозреваю (опять же, просто догадываюсь), что обработка не выполняется на уровне ядра.

У меня был существующий код для тестирования семафоров для целей IPC, поэтому я сравнил две ситуации (одна с использованием semop, а другая с lockf).Я сделал тест бедняка и просто побежал к экземплярам приложения.Общий семафор был использован для синхронизации запуска.При выполнении теста semop оба процесса завершили 3 миллиона циклов почти синхронно.Цикл lockf, с другой стороны, был не таким справедливым.Один процесс обычно завершается, в то время как другой завершил только половину циклов.

Цикл для теста semop выглядел следующим образом.Функции semwait и semsignal являются просто оболочками для вызовов semop.

   ct = myclock();
   for ( i = 0; i < loops; i++ )
      {
      ret = semwait( "test", semid, 0 );
      if ( ret < 0 ) { perror( "semwait" ); break; }

      if (( i & 0x7f ) == 0x7f )
         printf( "\r%d%%", (int)(i * 100.0 / loops ));

      ret = semsignal( semid, 0 );
      if ( ret < 0 ) { perror( "semsignal" ); break; }
      }
   printf( "\nsemop time: %d ms\n", myclock() - ct );

Общее время выполнения для обоих методов было примерно одинаковым, хотя версия lockf фактически иногда была быстрее в целом, потому чтонесправедливости планирования.Как только первый процесс завершится, другой процесс получит неоспоримый доступ в течение около 1,5 миллионов итераций и будет работать очень быстро.

При работе без всяких сомнений (получение и снятие блокировок одним процессом) версия semop работала быстрее.Это заняло около 2 секунд за 1 миллион итераций, в то время как версия lockf заняла около 3 секунд.

Это выполнялось в следующей версии:

[]$ uname -r
2.6.11-1.1369_FC4smp
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...