una proyectos programacion programa pasos para paquete nombre hacer ejemplos dev crear como cambiar aplicacion c++ c linux single-instance

pasos - proyectos de programacion en dev c++



Cómo crear una aplicación de instancia única en C o C++ (12)

¿Cuál sería su sugerencia para crear una aplicación de instancia única, de modo que solo se permita ejecutar un proceso a la vez? ¿Bloqueo de archivos, mutex o qué?


Acabo de escribir uno y probé.

#define PID_FILE "/tmp/pidfile" static void create_pidfile(void) { int fd = open(PID_FILE, O_RDWR | O_CREAT | O_EXCL, 0); close(fd); } int main(void) { int fd = open(PID_FILE, O_RDONLY); if (fd > 0) { close(fd); return 0; } // make sure only one instance is running create_pidfile(); }


Aquí hay una solución basada en sem_open

/* *compile with : *gcc single.c -o single -pthread */ /* * run multiple instance on ''single'', and check the behavior */ #include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <semaphore.h> #include <unistd.h> #include <errno.h> #define SEM_NAME "/mysem_911" int main() { sem_t *sem; int rc; sem = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 1); if(sem==SEM_FAILED){ printf("sem_open: failed errno:%d/n", errno); } rc=sem_trywait(sem); if(rc == 0){ printf("Obtained lock !!!/n"); sleep(10); //sem_post(sem); sem_unlink(SEM_NAME); }else{ printf("Lock not obtained/n"); } }

Uno de los comentarios sobre una respuesta diferente dice "Encontré sem_open () bastante deficiente". No estoy seguro sobre los detalles de lo que falta


Aquí hay una solución en C ++. Utiliza la recomendación de socket de Maxim. Me gusta más esta solución que la solución de bloqueo basada en archivos, porque la basada en archivos falla si el proceso falla y no elimina el archivo de bloqueo. Otro usuario no podrá eliminar el archivo y bloquearlo. Los sockets se eliminan automáticamente cuando el proceso finaliza.

Uso:

int main() { SingletonProcess singleton(5555); // pick a port number to use that is specific to this app if (!singleton()) { cerr << "process running already. See " << singleton.GetLockFileName() << endl; return 1; } ... rest of the app }

Código:

#include <netinet/in.h> class SingletonProcess { public: SingletonProcess(uint16_t port0) : socket_fd(-1) , rc(1) , port(port0) { } ~SingletonProcess() { if (socket_fd != -1) { close(socket_fd); } } bool operator()() { if (socket_fd == -1 || rc) { socket_fd = -1; rc = 1; if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { throw std::runtime_error(std::string("Could not create socket: ") + strerror(errno)); } else { struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons (port); name.sin_addr.s_addr = htonl (INADDR_ANY); rc = bind (socket_fd, (struct sockaddr *) &name, sizeof (name)); } } return (socket_fd != -1 && rc == 0); } std::string GetLockFileName() { return "port " + std::to_string(port); } private: int socket_fd = -1; int rc; uint16_t port; };


Dependerá del problema que desee evitar al forzar a su aplicación a tener solo una instancia y el alcance en el que considere las instancias.

Para un deamon: la forma habitual es tener un archivo /var/run/app.pid .

Para la aplicación del usuario, tuve más problemas con las aplicaciones que me impidieron ejecutarlas dos veces que con la posibilidad de ejecutar dos veces una aplicación que no debería haberse ejecutado. Entonces, la respuesta sobre "por qué y en qué alcance" es muy importante y probablemente traerá una respuesta específica sobre el por qué y el alcance deseado.


En base a las sugerencias en la respuesta de la máxima aquí está mi solución POSIX de un demonio de doble función (es decir, una aplicación única que puede actuar como daemon y como un cliente que se comunica con ese daemon). Este esquema tiene la ventaja de proporcionar una solución elegante del problema cuando la instancia que se inició primero debe ser el daemon y todas las siguientes ejecuciones deberían simplemente cargar el trabajo en ese daemon. Es un ejemplo completo pero carece de muchas cosas que un daemon real debería hacer (por ejemplo, usar syslog para registrar y fork para ponerse en segundo plano correctamente , descartar privilegios, etc.), pero ya es bastante largo y está funcionando como está. Hasta ahora, solo he probado esto en Linux, pero IIRC debería ser compatible con POSIX.

En el ejemplo, los clientes pueden enviar enteros pasados ​​a ellos como primer argumento de línea de comando y analizados por atoi través del socket al daemon que lo imprime en stdout . Con este tipo de enchufes también es posible transferir matrices, estructuras e incluso descriptores de archivos (ver man 7 unix ).

#include <stdio.h> #include <stddef.h> #include <stdbool.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/socket.h> #include <sys/un.h> #define SOCKET_NAME "/tmp/exampled" static int socket_fd = -1; static bool isdaemon = false; static bool run = true; /* returns * -1 on errors * 0 on successful server bindings * 1 on successful client connects */ int singleton_connect(const char *name) { int len, tmpd; struct sockaddr_un addr = {0}; if ((tmpd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { printf("Could not create socket: ''%s''./n", strerror(errno)); return -1; } /* fill in socket address structure */ addr.sun_family = AF_UNIX; strcpy(addr.sun_path, name); len = offsetof(struct sockaddr_un, sun_path) + strlen(name); int ret; unsigned int retries = 1; do { /* bind the name to the descriptor */ ret = bind(tmpd, (struct sockaddr *)&addr, len); /* if this succeeds there was no daemon before */ if (ret == 0) { socket_fd = tmpd; isdaemon = true; return 0; } else { if (errno == EADDRINUSE) { ret = connect(tmpd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (ret != 0) { if (errno == ECONNREFUSED) { printf("Could not connect to socket - assuming daemon died./n"); unlink(name); continue; } printf("Could not connect to socket: ''%s''./n", strerror(errno)); continue; } printf("Daemon is already running./n"); socket_fd = tmpd; return 1; } printf("Could not bind to socket: ''%s''./n", strerror(errno)); continue; } } while (retries-- > 0); printf("Could neither connect to an existing daemon nor become one./n"); close(tmpd); return -1; } static void cleanup(void) { if (socket_fd >= 0) { if (isdaemon) { if (unlink(SOCKET_NAME) < 0) printf("Could not remove FIFO./n"); } else close(socket_fd); } } static void handler(int sig) { run = false; } int main(int argc, char **argv) { switch (singleton_connect(SOCKET_NAME)) { case 0: { /* Daemon */ struct sigaction sa; sa.sa_handler = &handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) != 0 || sigaction(SIGQUIT, &sa, NULL) != 0 || sigaction(SIGTERM, &sa, NULL) != 0) { printf("Could not set up signal handlers!/n"); cleanup(); return EXIT_FAILURE; } struct msghdr msg = {0}; struct iovec iovec; int client_arg; iovec.iov_base = &client_arg; iovec.iov_len = sizeof(client_arg); msg.msg_iov = &iovec; msg.msg_iovlen = 1; while (run) { int ret = recvmsg(socket_fd, &msg, MSG_DONTWAIT); if (ret != sizeof(client_arg)) { if (errno != EAGAIN && errno != EWOULDBLOCK) { printf("Error while accessing socket: %s/n", strerror(errno)); exit(1); } printf("No further client_args in socket./n"); } else { printf("received client_arg=%d/n", client_arg); } /* do daemon stuff */ sleep(1); } printf("Dropped out of daemon loop. Shutting down./n"); cleanup(); return EXIT_FAILURE; } case 1: { /* Client */ if (argc < 2) { printf("Usage: %s <int>/n", argv[0]); return EXIT_FAILURE; } struct iovec iovec; struct msghdr msg = {0}; int client_arg = atoi(argv[1]); iovec.iov_base = &client_arg; iovec.iov_len = sizeof(client_arg); msg.msg_iov = &iovec; msg.msg_iovlen = 1; int ret = sendmsg(socket_fd, &msg, 0); if (ret != sizeof(client_arg)) { if (ret < 0) printf("Could not send device address to daemon: ''%s''!/n", strerror(errno)); else printf("Could not send device address to daemon completely!/n"); cleanup(); return EXIT_FAILURE; } printf("Sent client_arg (%d) to daemon./n", client_arg); break; } default: cleanup(); return EXIT_FAILURE; } cleanup(); return EXIT_SUCCESS; }


Nadie lo ha mencionado, pero sem_open() crea un semáforo con nombre real bajo los sistemas operativos modernos que cumplen con POSIX. Si le das a un semáforo un valor inicial de 1, se convierte en un mutex (siempre y cuando se libere estrictamente solo si se obtuvo con éxito un bloqueo).

Con varios objetos basados ​​en sem_open() , puede crear todos los objetos con nombre de Windows equivalentes comunes: mutexes con nombre, semáforos con nombre y eventos con nombre. Los eventos denominados con "manual" establecido en verdadero es un poco más difícil de emular (se requieren cuatro objetos de semáforo para emular correctamente CreateEvent() , SetEvent() y ResetEvent() ). De todos modos, estoy divagando.

Alternativamente, hay memoria compartida nombrada. Puede inicializar un mutex pthread con el atributo "proceso compartido" en la memoria compartida con nombre y luego todos los procesos pueden acceder de forma segura a ese objeto mutex después de abrir un identificador a la memoria compartida con shm_open() / mmap() . sem_open() es más fácil si está disponible para su plataforma (si no es así, debería ser por motivos de cordura).

Independientemente del método que utilice, para probar una instancia única de su aplicación, use la variante trylock() de la función de espera (por ejemplo, sem_trywait() ). Si el proceso es el único en ejecución, se bloqueará exitosamente el mutex. Si no es así, fallará de inmediato.

No olvides desbloquear y cerrar el mutex al salir de la aplicación.


Para Windows, un objeto kernel con nombre (por ejemplo, CreateEvent, CreateMutex). Para unix, un archivo pid: crea un archivo y escribe tu ID de proceso en él.


Parece que no se menciona: es posible crear un mutex en la memoria compartida, pero debe marcarse como compartido por atributos (no probados):

pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_mutex_t *mutex = shmat(SHARED_MEMORY_ID, NULL, 0); pthread_mutex_init(mutex, &attr);

También hay semáforos de memoria compartida (pero no pude encontrar cómo bloquear uno):

int sem_id = semget(SHARED_MEMORY_KEY, 1, 0);


Puede crear un socket AF_UNIX de "espacio de nombre anónimo". Esto es completamente específico de Linux, pero tiene la ventaja de que no debe existir ningún sistema de archivos.

Lea la página de manual de Unix (7) para obtener más información.


Simplemente ejecute este código en un hilo separado:

void lock() { while(1) { ofstream closer("myapplock.locker", ios::trunc); closer << "locked"; closer.close(); } }

Ejecuta esto como tu código principal:

int main() { ifstream reader("myapplock.locker"); string s; reader >> s; if (s != "locked") { //your code } return 0; }


Una buena forma es:

#include <sys/file.h> #include <errno.h> int pid_file = open("/var/run/whatever.pid", O_CREAT | O_RDWR, 0666); int rc = flock(pid_file, LOCK_EX | LOCK_NB); if(rc) { if(EWOULDBLOCK == errno) ; // another instance is running } else { // this is the first instance }

Tenga en cuenta que el bloqueo le permite ignorar archivos pid obsoletos (es decir, no tiene que eliminarlos). Cuando la aplicación finaliza por cualquier motivo, el sistema operativo libera el bloqueo de archivos por usted.

Los archivos Pid no son muy útiles porque pueden estar obsoletos (el archivo existe pero el proceso no). Por lo tanto, el ejecutable de la aplicación en sí mismo puede bloquearse en lugar de crear y bloquear un archivo pid.

Un método más avanzado es crear y enlazar un socket de dominio Unix usando un nombre de socket predefinido. Bind tiene éxito en la primera instancia de su aplicación. Una vez más, el sistema operativo desenlaza el socket cuando la aplicación termina por algún motivo. Cuando bind() falla, otra instancia de la aplicación puede connect() y usar este socket para pasar sus argumentos de línea de comando a la primera instancia.


Evite el bloqueo basado en archivos

Siempre es bueno evitar un mecanismo de bloqueo basado en archivos para implementar la instancia única de una aplicación. El usuario siempre puede cambiar el nombre del archivo de bloqueo a un nombre diferente y ejecutar la aplicación nuevamente de la siguiente manera:

mv lockfile.pid lockfile1.pid

Donde lockfile.pid es el archivo de bloqueo basado en el cual se verifica su existencia antes de ejecutar la aplicación.

Por lo tanto, siempre es preferible utilizar un esquema de bloqueo en el objeto directamente visible solo para el kernel. Entonces, cualquier cosa que tenga que ver con un sistema de archivos no es confiable.

Entonces, la mejor opción sería enlazar a un socket inet. Tenga en cuenta que los sockets de dominio de Unix residen en el sistema de archivos y no son confiables.

Alternativamente, también puede hacerlo usando DBUS.