linux - porque - ¿Cómo recupero un semáforo cuando el proceso que lo redujo a cero se bloquea?
popurri de libre yo soy libre (7)
Deberá verificarlo dos veces, pero creo que se puede llamar a sem_post desde un manejador de señal. Si puede detectar algunas de las situaciones que están reduciendo 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 restablecerlo. Presumiblemente, usted sabe cuándo su sistema se ha estancado. Puede bajarlo y ejecutar el programa de utilidad.
Además, el teléfono móvil se muestra generalmente en / dev / shm y puedes eliminarlo.
Los semáforos SysV son más complacientes para este escenario. Puede especificar SEM_UNDO, en el cual el sistema retrocederá los cambios al semáforo creado por un proceso si muere. También tienen la capacidad de decirle la última identificación del proceso para alterar el semáforo.
Tengo varias aplicaciones compiladas con g ++, que se ejecutan en Ubuntu. Estoy usando semáforos con nombre 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 disminuir el semáforo y luego se bloquea o se sem_post()
-9 antes de que tenga la oportunidad de llamar a sem_post()
, a partir de ese momento , el semáforo con nombre es "inutilizable".
Por "inutilizable", lo que quiero decir es que el recuento de semáforos ahora es cero, y el proceso que debería haber 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 estoy perdiendo una API en alguna parte?
Aquí es cómo abro el semáforo con nombre:
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
Aquí es cómo lo disminuyo:
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";
}
Deberías poder encontrarlo desde el caparazón usando lsof
. ¿Entonces posiblemente puedas eliminarlo?
Actualizar
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.
Este es un problema típico cuando se gestionan semáforos. Algunos programas usan un solo proceso para administrar la inicialización / eliminación del semáforo. Por lo general, este proceso hace 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 el indicador SEM_UNDO en su llamada a semop ().
Pero, con la información que ha proporcionado, sugeriría que no use semáforos. Especialmente si su proceso está en peligro de ser asesinado o estrellarse. Intente usar algo que el sistema operativo limpiará automágicamente para usted.
Resulta que no hay una manera confiable de recuperar el semáforo. Claro, cualquiera puede post_sem()
al semáforo designado para que el recuento vuelva a aumentar el cero pasado, 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 de ipc también disponibles: las herramientas comunes ipcmk
, ipcrm
e ipcs
son solo para los semáforos SysV desactualizados. Específicamente, no funcionan con los nuevos semáforos POSIX.
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 atraparse en un manejador de señal. Dos ejemplos: un socket de escucha vinculado 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. Entonces, en lugar de una sem_wait()
y sem_post()
, estoy usando:
lockf( fd, F_LOCK, 0 )
y
lockf( fd, F_ULOCK, 0 )
Cuando la aplicación sale de alguna manera, el archivo se cierra automáticamente, lo que también libera el bloqueo del archivo. Otras aplicaciones cliente que esperan el "semáforo" son libres de proceder como se espera.
Gracias por la ayuda chicos.
Si el proceso fue ASESINADO, entonces no habrá una forma directa de determinar si se ha ido.
Podrías operar algún tipo de comprobación de integridad periódica en todos los semáforos que tengas: usa semctl (cmd = GETPID) para encontrar el PID para el último proceso que tocó cada semáforo en el estado que describes, luego verifica si ese proceso aún existe. Si no, realice la limpieza.
Simplemente haga un sem_unlink()
inmediatamente después del sem_open()
. Linux se eliminará después de que todos los procesos hayan cerrado el recurso, que incluye cierres internos.
Utilice un archivo de bloqueo en lugar de un semáforo, al igual que la solución de @ Stéphane, pero sin las llamadas flock (). Simplemente puede abrir el archivo con un candado 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");