Как мне восстановить семафор, когда процесс, который уменьшил его до нуля, падает? - PullRequest
46 голосов
/ 13 января 2010

У меня есть несколько приложений, скомпилированных с g ++, работающих в Ubuntu. Я использую именованные семафоры для координации различных процессов.

Все отлично работает , за исключением , в следующей ситуации: если один из процессов вызывает sem_wait() или sem_timedwait() для уменьшения семафора, а затем падает или его убивают -9, прежде чем он получает возможность вызвать sem_post(), затем с того момента именованный семафор становится «непригодным для использования».

Под "непригодным" я имею в виду то, что счетчик семафоров теперь равен нулю, а процесс, который должен был увеличить его до 1, умер или был убит.

Я не могу найти sem_*() API, который мог бы сообщить мне, что процесс, который последний раз уменьшил его, завершился сбоем.

Я где-то пропускаю API?

Вот как я могу открыть названный семафор:

sem_t *sem = sem_open( "/testing",
    O_CREAT     |   // create the semaphore if it does not already exist
    O_CLOEXEC   ,   // close on execute
    S_IRWXU     |   // permissions:  user
    S_IRWXG     |   // permissions:  group
    S_IRWXO     ,   // permissions:  other
    1           );  // initial value of the semaphore

Вот как я уменьшаю его:

struct timespec timeout = { 0, 0 };
clock_gettime( CLOCK_REALTIME, &timeout );
timeout.tv_sec += 5;

if ( sem_timedwait( sem, &timeout ) )
{
    throw "timeout while waiting for semaphore";
}

Ответы [ 7 ]

38 голосов
/ 13 января 2010

Оказывается, нет способа надежно восстановить семафор. Конечно, любой может post_sem() на указанный семафор, чтобы снова увеличить счет после нуля, но как определить, когда необходимо такое восстановление? Предоставленный API слишком ограничен и никоим образом не указывает, когда это произошло.

Остерегайтесь также доступных инструментов ipc - общие инструменты ipcmk, ipcrm и ipcs предназначены только для устаревших семафоров SysV. Они специально не работают с новыми семафорами POSIX.

Но, похоже, есть и другие вещи, которые можно использовать для блокировки вещей, которые операционная система автоматически освобождает, когда приложение умирает так, что не может быть поймано обработчиком сигнала. Два примера: прослушивающий сокет, привязанный к определенному порту, или блокировка для определенного файла.

Я решил, что блокировка файла - это то решение, которое мне нужно. Поэтому вместо вызовов sem_wait() и sem_post() я использую:

lockf( fd, F_LOCK, 0 )

и

lockf( fd, F_ULOCK, 0 )

Когда приложение закрывается любым способом, файл автоматически закрывается, что также снимает блокировку файла. Другие клиентские приложения, ожидающие «семафора», могут свободно работать, как и ожидалось.

Спасибо за помощь, ребята.

6 голосов
/ 09 марта 2013

Используйте файл блокировки вместо семафора, очень похоже на решение @ Stéphane, но без вызовов flock (). Вы можете просто открыть файл, используя эксклюзивную блокировку:

//call to open() will block until it can obtain an exclusive lock on the file.
errno = 0;
int fd = open("/tmp/.lockfile", 
    O_CREAT | //create the file if it's not present.
    O_WRONLY | //only need write access for the internal locking semantics.
    O_EXLOCK, //use an exclusive lock when opening the file.
    S_IRUSR | S_IWUSR); //permissions on the file, 600 here.

if (fd == -1) {
    perror("open() failed");
    exit(EXIT_FAILURE);
}

printf("Entered critical section.\n);
//Do "critical" stuff here.

//exit the critical section
errno = 0;
if (close(fd) == -1) {
    perror("close() failed");
    exit(EXIT_FAILURE);
}

printf("Exited critical section.\n");
5 голосов
/ 13 января 2010

Это типичная проблема при управлении семафорами. Некоторые программы используют один процесс для управления инициализацией / удалением семафора. Обычно этот процесс делает только это и ничего больше. Ваши другие приложения могут ждать, пока семафор не станет доступен. Я видел, как это делается с помощью API-интерфейса типа SYSV, но не с помощью POSIX. Аналогично тому, что упоминалось в « Duck », с использованием флага SEM_UNDO в вызове semop ()


Но, с информацией, которую вы предоставили, я бы посоветовал вам не использовать семафоры. Особенно, если ваш процесс может быть убит или разрушен. Попробуйте использовать то, что ОС автоматически очистит для вас.

2 голосов
/ 13 января 2010

Вам нужно будет дважды проверить, но я считаю, что sem_post можно вызвать из обработчика сигнала. Если вам удастся уловить некоторые ситуации, которые приводят к остановке процесса, это может помочь.

В отличие от мьютекса, любой процесс или поток (с разрешениями) могут публиковать в семафор. Вы можете написать простую утилиту для сброса. Предположительно, вы знаете, когда ваша система заблокирована. Вы можете отключить его и запустить служебную программу.

Также semaphone обычно указан в / dev / shm, и вы можете удалить его.

Семафоры SysV более приспособлены для этого сценария. Вы можете указать SEM_UNDO, в котором система будет отменять изменения семафора, сделанные процессом, если он умирает. У них также есть возможность сообщить вам последний идентификатор процесса для изменения семафора.

2 голосов
/ 13 января 2010

Вы сможете найти его из оболочки, используя lsof. Тогда, возможно, вы можете удалить его?

Обновление

Ах да ... man -k semaphore на помощь.

Кажется, вы можете использовать ipcrm, чтобы избавиться от семафора. Кажется, вы не первый с этой проблемой.

1 голос
/ 13 января 2010

Если процесс был убит, то не будет прямого способа определить, что он ушел.

Вы можете выполнить какую-то периодическую проверку целостности всех имеющихся у вас семафоров - используйте semctl (cmd = GETPID), чтобы найти PID для последнего процесса, который коснулся каждого семафора в состоянии, которое вы описываете, затем проверьте, продолжается ли этот процесс. Если нет, выполните очистку.

0 голосов
/ 16 марта 2011

Просто сделайте sem_unlink() сразу после sem_open(). Linux будет удален после того, как все процессы закрыли ресурс, который включает внутренние закрытия.

...