¿Cómo puedo recuperar un semáforo cuando el proceso que reduce a cero los accidentes?


Tengo varias aplicaciones compiladas con g++, ejecutándose en Ubuntu. Estoy usando semáforos nombrados para coordinar entre diferentes procesos.

Todo funciona bien excepto en la siguiente situación: Si uno de los procesos llama a sem_wait() o sem_timedwait() para decrementar el semáforo y luego se bloquea o muere -9 antes de que tenga la oportunidad de llamar a sem_post(), entonces a partir de ese momento, el semáforo nombrado es "inutilizable".

Por "inutilizable", lo que quiero decir es que el recuento de semáforos es ahora cero, y el proceso que debería haberlo incrementado de nuevo a 1 ha muerto o ha sido asesinado.

No puedo encontrar una API sem_*() que pueda decirme que el proceso que disminuyó por última vez se ha bloqueado.

¿Me falta una API en algún lugar?

Así es como abro el semáforo nombrado:

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

Así es como lo decremento:

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";
}
Author: Stéphane, 2010-01-13

7 answers

Resulta que no hay una manera confiable de recuperar el semáforo. Claro, cualquiera puede post_sem() al semáforo nombrado para conseguir que el conteo aumente más allá de cero otra vez, pero ¿cómo saber cuándo se necesita tal recuperación? La API proporcionada es demasiado limitada y no indica de ninguna manera cuando esto ha sucedido.

Tenga cuidado con las herramientas ipc también disponibles the las herramientas comunesipcmk, ipcrm, y ipcs son solo para los semáforos SysV obsoletos. Específicamente no funcionan con el nuevo POSIX semáforos.

Pero parece que hay otras cosas que se pueden usar para bloquear cosas, que el sistema operativo libera automáticamente cuando una aplicación muere de una manera que no puede ser atrapada en un manejador de señales. Dos ejemplos: un socket de escucha enlazado a un puerto en particular, o un bloqueo en un archivo específico.

Decidí que el bloqueo de un archivo es la solución que necesitaba. Así que en lugar de una llamada sem_wait() y sem_post(), estoy usando:

lockf( fd, F_LOCK, 0 )

Y

lockf( fd, F_ULOCK, 0 )

Cuando la aplicación sale de cualquier manera, el archivo se cierra automáticamente, lo que también libera el bloqueo de archivos. Otras aplicaciones cliente que esperan el "semáforo" son libres de proceder como se espera.

Gracias por la ayuda, chicos.

 35
Author: Stéphane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-13 17:04:27

Usa un fichero lock en lugar de un semáforo, como la solución de @Stéphane pero sin las llamadas flock (). Simplemente puede abrir el archivo usando un bloqueo exclusivo:

//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");
 6
Author: Raffi Khatchadourian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-12 20:57:15

Este es un problema típico al administrar semáforos. Algunos programas utilizan un solo proceso para gestionar la inicialización/eliminación del semáforo. Por lo general, este proceso hace solo esto y nada más. Sus otras aplicaciones pueden esperar hasta que el semáforo esté disponible. He visto esto hecho con la API de tipo SYSV, pero no con POSIX. Similar a lo que' Duck ' mencionó, usando la bandera SEM_UNDO en tu llamada a semop ().


Pero, con la información que has a condición de que sugeriría que no utilice semáforos. Especialmente si su proceso está en peligro de morir o estrellarse. Intente usar algo que el sistema operativo limpiará automáticamente para usted.

 4
Author: Steve Lazaridis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-13 14:26:42

Debería ser capaz de encontrarlo desde el shell usando lsof. Entonces, ¿es posible que puedas borrarlo?

Update

Ah sí... man -k semaphore al rescate.

Parece que puedes usar ipcrm para deshacerte de un semáforo. Parece que no eres el primero con este problema.

 2
Author: Carl Smotricz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-13 01:06:00

Tendrá que volver a comprobar, pero creo que sem_post se puede llamar desde un controlador de señal. Si usted es capaz de atrapar algunas de las situaciones que están derribando el proceso esto podría ayudar.

A diferencia de un mutex, cualquier proceso o subproceso (con permisos) puede publicar en el semáforo. Puede escribir una utilidad simple para restablecerla. Es de suponer que usted sabe cuando su sistema se ha estancado. Puede bajarlo y ejecutar el programa de utilidad.

También el semáfono suele aparecer en /dev / shm y puede eliminarlo.

Los semáforos SysV son más adecuados para este escenario. Puede especificar SEM_UNDO, en el que el sistema retrocederá los cambios en el semáforo realizados por un proceso si muere. También tienen la capacidad de decirle el último id de proceso para alterar el semáforo.

 2
Author: Duck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-13 02:51:20

Si el proceso fue asesinado, entonces no habrá ninguna forma directa de determinar que se ha ido.

Podría operar algún tipo de comprobación periódica de integridad en todos los semáforos que tenga - use semctl (cmd=GETPID) para encontrar el PID del último proceso que tocó cada semáforo en el estado que describe, luego verifique si ese proceso todavía está presente. Si no, realice la limpieza.

 1
Author: martin clayton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-13 01:18:01

Simplemente haga un sem_unlink() inmediatamente después del sem_open(). Linux eliminará después de que todos los procesos hayan cerrado el recurso, lo que incluye cierres internos.

 0
Author: gsb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-02 10:38:40