Как восстановить семафор, когда процесс, который уменьшил его до нуля, падает?
У меня есть несколько приложений, скомпилированных с g++, работающих в Ubuntu. Я использую названные семафоры для координации между различными процессами.
Все работает отлично, за исключением следующей ситуации: Если один из процессов вызывает sem_wait()
или sem_timedwait()
, чтобы уменьшить семафор, а затем сбой или был убит -9, прежде чем он получит возможность вызвать sem_post()
, то из в этот момент названный семафор "неприменим".
Под "непригодным", я имею в виду, что счет семафора теперь равен нулю, и процесс, который должен был увеличить его до 1, умер или был убит.
Я не могу найти API sem_*()
, который мог бы рассказать мне, что последний из них уменьшился.
Я где-то пропускаю 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";
}
Ответы
Ответ 1
Оказывается, нет надежного восстановления семафора. Конечно, любой может post_sem()
присвоить названному семафору, чтобы счет снова увеличился за ноль, но как сказать, когда такое восстановление необходимо? Предоставленный API слишком ограничен и никоим образом не указывает, когда это произошло.
Остерегайтесь доступных инструментов ipc - общие инструменты ipcmk
, ipcrm
и ipcs
предназначены только для устаревших семафоров SysV. Они специально не работают с новыми семафорами POSIX.
Но похоже, что есть другие вещи, которые можно использовать для блокировки вещей, которые операционная система автоматически освобождает, когда приложение умирает таким образом, что его невозможно поймать в обработчике сигналов. Два примера: прослушивающий сокет, привязанный к определенному порту, или блокировка для определенного файла.
Я решил, что блокировка файла - это необходимое мне решение. Поэтому вместо вызовов sem_wait()
и sem_post()
я использую:
lockf( fd, F_LOCK, 0 )
и
lockf( fd, F_ULOCK, 0 )
Когда приложение выходит каким-либо образом, файл автоматически закрывается, что также освобождает блокировку файла. Другие клиентские приложения, ожидающие "семафора", затем могут действовать так, как ожидалось.
Спасибо за помощь, ребята.
Ответ 2
Используйте файл блокировки вместо семафора, как и решение @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 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");
Ответ 3
Это типичная проблема при управлении семафорами. Некоторые программы используют один процесс для управления инициализацией/удалением семафора. Обычно этот процесс делает именно это и ничего больше. Ваши другие приложения могут ждать, пока семафор не будет доступен. Я видел это с API типа SYSV, но не с POSIX. Подобно тому, что упоминалось в " Duck", используя флаг SEM_UNDO в вашем вызове semop().
Но, с информацией, которую вы предоставили, я предлагаю вам не использовать семафоры. Особенно, если ваш процесс находится в опасности быть убитым или сбой. Попытайтесь использовать что-то, что ОС будет автоматически автоматизировать для вас.
Ответ 4
Вы можете найти его из оболочки с помощью lsof
. Тогда, возможно, вы можете удалить его?
Обновление
А да... man -k semaphore
на помощь.
Кажется, вы можете использовать ipcrm
, чтобы избавиться от семафора. Кажется, вы не первый с этой проблемой.
Ответ 5
Вам нужно будет дважды проверить, но я верю, что sem_post можно вызвать из обработчика сигнала. Если вы можете поймать некоторые ситуации, которые приводят к снижению процесса, это может помочь.
В отличие от мьютекса любой процесс или поток (с разрешениями) могут отправлять сообщения в семафор. Вы можете написать простую утилиту для reset it. Предположительно, вы знаете, когда ваша система зашла в тупик. Вы можете снести его и запустить служебную программу.
Также семафон обычно указан в /dev/shm, и вы можете его удалить.
Семафоры SysV более подходят для этого сценария. Вы можете указать SEM_UNDO, в котором система вернет изменения в семафор, созданный процессом, если он умрет. Они также могут сообщить вам последний идентификатор процесса, чтобы изменить семафор.
Ответ 6
Если процесс был KILLED, тогда не будет никакого прямого способа определить, что он ушел.
Вы можете использовать некоторую периодическую проверку целостности всех семафоров, которые у вас есть, - используйте semctl (cmd = GETPID), чтобы найти PID для последнего процесса, который касался каждого семафора в описываемом вами состоянии, а затем проверить, все ли еще этот процесс. Если нет, выполните очистку.
Ответ 7
Просто выполните sem_unlink()
сразу после sem_open()
. Linux удалит после того, как все процессы закроют ресурс, который включает внутренние закрытия.