2010-01-13 1 views
39

У меня есть несколько приложений, скомпилированных с 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"; 
} 

ответ

32

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

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

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

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

lockf(fd, F_LOCK, 0) 

и

lockf(fd, F_ULOCK, 0) 

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

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

+1

+1, в конечном итоге делали то же самое, семафоры бесполезны в таких сценариях –

+0

Кто-то отправил мне электронное письмо, чтобы узнать больше. Я написал небольшую запись в блоге почти 3 года назад, когда столкнулся с этой проблемой. Более подробную информацию о том, как я решил это с блокировкой файлов, можно найти здесь: http://charette.no-ip.com:81/programming/2010-01-13_PosixSemaphores/index.html –

+0

Можно ли сделать то же самое, просто открыв закрытие файла? Я нашел это на странице man для open(): «Когда вы открываете файл, семантика блокировки с помощью flock (2) может быть получена путем установки O_SHLOCK для общей блокировки или O_EXLOCK для исключительной блокировки». –

2

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

Update

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

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

+1

Да, я знаю об ipcrm, но это не помогает. Если бы я знал, что семафор был потерян, я мог бы так же легко sem_post(), чтобы «вернуть его». Кажется, что проблема не связана с тем, что событие, последнее из которого уменьшилось, было убито. –

+1

Кроме того, только что заметили на странице man, что ipcrm работает только на старых семафорах SysV, а не на семафорах POSIX. То же самое с ipcs. –

1

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

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

+0

Что-то вроде этих строк - это то, что я искал, но, конечно же, для семафоров POSIX, которые вы найдете в #include . Из того, что я могу сказать, стиль вызовов semctl() специфичен для старых семафоров SysV от . –

2

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

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

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

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

+1

Некоторые сигналы, такие как kill -9, обходят сигнальные манипуляторы, в которых я столкнулся. У меня есть обработчик сигналов для тех, которые я могу уловить, а в деструкторе для объекта на основе области я вызываю sem_post(), когда пакет распаковывается. Но те немногие затяжные неуловимые сигналы - это то, что я надеялся решить. –

+1

Я думаю, что справедливый вопрос - спросить, кто является пользователем, и почему они убивают приложение таким образом?Вы можете попробовать маршрут SysV или даже блокировки файлов, которые должны возвращаться, когда процесс умирает. – Duck

+0

Собственно, это то, что я решил сделать прошлой ночью. Поскольку файлы, которые были открыты() и lockf(), автоматически освобождаются, когда приложения убиты -9, этот метод «общения» фактически работает более надежно, чем семафоры, учитывая то, что мне нужно для координации. –

4

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


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

-1

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

+1

Не будет ли причиной смены имени семафора? Я не хотел его удалять, я хотел опубликовать его, когда один из приложений разбился или был убит. (Несколько приложений, которые используются здесь, все работают с одним и тем же набором семафоров, чтобы координировать некоторые внутренние работы.) –

+2

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

5

Используйте файл блокировки вместо семафора, как и решение @ 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"); 
+1

Хороший код с 1 модификацией: вы должны создать файл блокировки до начала разговора и, конечно же, сохранить связь. В противном случае, в моих тестах в Mac OS X 10.10 DP5, open() может преуспеть для двух одноранговых процессов, конкурирующих с первоначальным созданием файла, если в течение нескольких миллисекунд. Проблема возникает либо с кодом Стефана, либо с Раффи. Я тогда испытывал стресс. Результат: код Раффи работал отлично, код Стефана не совсем. Я не изучал, почему. Если вас интересует, см. Https://github.com/jerrykrinock/ClassesObjC/blob/master/SSYSemaphore.h и .m. –

+0

@JerryKrinock Но, не 'open()' создает файл блокировки, если он отсутствует (когда задан флаг O_CREAT)? –

+1

Я собираюсь снимать с бедра, потому что у меня нет 30 минут, чтобы потребовалось правильно освежить мое понимание этого сейчас. Я думаю, что ответ: да, open() с O_CREAT создаст файл, если это необходимо, но если два процесса выполнят open() в течение нескольких миллисекунд друг от друга, результаты непредсказуемы. Поэтому мое предложение создать файл блокировки до него; Хорошо, я добавлю, если только это не будет нормально, если первое утверждение будет отброшенным. –

Смежные вопросы