2011-10-14 28 views
5

Estoy usando Qt y tratando de lograr una aplicación de instancia única aplicando la solución this en Linux (ubuntu). El problema es que si la aplicación termina inesperadamente (falla segmentada o el usuario la mata), la memoria compartida permanece adjunta y ningún otro proceso puede crearla nuevamente. Recordar de QSharedMemory doc:C++ pérdida de memoria compartida, ¿cómo borrar la memoria compartida?

Unix: QSharedMemory "posee" el segmento de memoria compartida. Cuando el último subproceso o proceso que tiene una instancia de QSharedMemory asociada a un segmento de memoria compartida particular se separa del segmento por destruyendo su instancia de QSharedMemory, el kernel de Unix libera el segmento de memoria compartida . Pero si ese último subproceso o proceso falla sin ejecutar el destructor QSharedMemory, el segmento de memoria compartida sobrevive al bloqueo.

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 

    // Ensure single instanse of Cevirgec application 
    QSharedMemory shared(ApplicationConstants:: 

    if(!shared.create(512, QSharedMemory::ReadWrite)) 
    { 
     // QMessageBox msgBox; 
     QMessageBox::critical(0, QObject::tr("application is already running!"), QObject::tr("application is already running!"), QMessageBox::Ok, QMessageBox::Ok); 
     qCritical() << "application is already running!"; 

     exit(0); 
    } 
    else { 
     qDebug() << "application staring..."; 
    } 
    return a.exec(); 
} 

¿Qué soluciones puede sugerir aquí? ¿Cómo puedo asegurar que la memoria compartida se borre (o cualquier verbo utilizado en general) después de que el proceso finalmente termine? Necesito algo así como finally en Java todo alrededor de la función principal:/

EDIT: (Solución)

He conseguido el comportamiento deseado mediante el uso de QSharedMemory y la captura de SIGSEGV luego llamar sharedMemory.detach() en el manejador de señal.

+0

Probablemente no deba tratar de hacer una aplicación que se comporte de manera diferente a como el SO y los usuarios esperan que se comporten las aplicaciones. Si el comportamiento estándar para el sistema operativo es tal que los usuarios esperan poder lanzar varias instancias, como en Windows y Linux, entonces deberían ser capaces de hacerlo. Si el comportamiento estándar es forzar una instancia única, como en Mac, entonces permita que el sistema operativo lo haga cumplir por sí mismo. – bames53

+2

bien, no existe un estándar para tales comportamientos en sistemas operativos. Es totalmente dependiente de la aplicación – destan

+0

bames53: hay toneladas de aplicaciones que solo te permiten iniciar una instancia, y a veces es lo lógico. – rubenvb

Respuesta

4

Puede detectar las señales que bloquean su programa y utilizar un controlador que llama al destructor QSharedMemory.

+0

puedo llamar a un destructor manualmente – destan

+0

Sí, p. para un objeto f de la clase Foo: 'f. ~ Foo();'. Pero solo debe hacerlo en circunstancias en las que el lenguaje tampoco llame al destructor automáticamente. – bames53

+0

Sí, puede - pero no sé si puede depender de cualquier cosa relacionada con qt en el manejador de señal, ya que dichos manejadores deberían ser autocontenidos y no transmitirse a ninguna otra cosa en el programa. –

1

La verdad es que si su programa necesita ser eliminado o tiene una segfault, entonces realmente no puede hacer nada al respecto. La memoria compartida no es la mejor opción para garantizar una instancia única de la aplicación en UNIX/Linux. Intente utilizar semáforos en su lugar, ya que se cierran tan pronto como finaliza su aplicación.

EDIT:

De la documentación de sem_close

todos abiertos llamado semáforos están cerradas automáticamente en proceso de la terminación o execve (2).

También debo añadir que asegurar contraint de una sola aplicación podría tener consecuencias extrañas en el sistema como Linux - imaginar a alguien conectado a través de ssh con X túnel y tratando de iniciar su aplicación - si alguien ya lo está usando, no lo hará comienzo. Esto será bastante confisunig Eres desarrollador de aplicaciones y debes saberlo mejor si necesitas un bloqueo por sistema por usuario o incluso por sesión X.

Si desea utilizar el bloqueo por usuario, la solución podría ser agregar el archivo oculto en el directorio de inicio del usuario que contiene el pid actual. La siguiente aplicación buscará este archivo y, si existe, el enlace AND/proc/[pid]/exe apunta al binario actual, y luego devuelve el error.

+0

Pero creo que necesito un semáforo en todo el sistema, ¿verdad? – destan

+0

semáforos con nombre para ser exactos –

+0

pero no se plantea el mismo problema, si el semáforo permanece estrellado adquirió – destan

1

Siempre puede ejecutar un script después de la finalización de su programa para borrar manualmente la memoria compartida, semáforos, etc. en su sistema (el mío es un Mac Pro que ejecuta 10.8). He insertado un script que uso para esto al ejecutar programas que usan QSharedMemory, y lo uso cuando el programa se cierra inesperadamente y deja las instancias de memoria compartida "colgando".

Tenga en cuenta que esto eliminará todas las instancias de memoria compartida asociadas con su nombre de usuario. Si tiene varios programas ejecutándose y usando instancias de memoria compartida, debe esperar hasta que cada programa finalice o ajustar la secuencia de comandos según sea necesario para eliminar solo las instancias de memoria compartida que fueron creadas por su programa.

#!/bin/bash 

ME=$(whoami) 

IPCS_S=$(ipcs -s | grep $ME | sed "s///g" | cut -f2 -d " ") 
IPCS_M=$(ipcs -m | grep $ME | sed "s///g" | cut -f2 -d " ") 
IPCS_Q=$(ipcs -q | grep $ME | sed "s///g" | cut -f2 -d " ") 

echo "Clearing Semaphores" 
for id in $IPCS_S 
do 
    ipcrm -s $id 
done 

echo "Clearing Shared Memory" 
for id in $IPCS_M 
do 
    ipcrm -m $id 
done 

echo "Clearing Message Queues" 
for id in $IPCS_Q 
do 
    ipcrm -q $id 
done 
Cuestiones relacionadas