Windows tiene una opción para abrir un archivo con derechos de acceso exclusivos. Unix no.

Con el fin de garantizar el acceso exclusivo a algunos de archivo o dispositivo, es práctica común en Unix para utilizar un archivo de bloqueo generalmente se almacenan en el directorio /var/lock directorio.

La C instrucción open( "/var/lock/myLock.lock", O_RDWR | O_CREAT | O_EXCL, 0666 ) devuelve -1 si el bloqueo de archivo ya existe, de lo contrario, se crea. La función es atómica y asegura que no hay condición de carrera.

Cuando el recurso se libera el bloqueo de archivo es eliminado por la siguiente instrucción
remove( "/var/lock/myLock.lock" ).

Hay dos problemas con este método.

  1. El programa puede terminar sin quitar el bloqueo. Por ejemplo, porque se muere, se bloquea o lo que sea. El archivo de bloqueo permanece en su lugar, y evitar cualquier acceso a los recursos, incluso aunque ya no se utiliza.

  2. El bloqueo de archivo se ha creado el grupo y el mundo de la escritura de privilegio, sino que es una práctica común para configurar las cuentas a utilizar una máscara de permisos que limpiar el grupo mundial y permiso de escritura. Por lo tanto, si tuviéramos un método fiable para determinar que el bloqueo es huérfano (no usar), un usuario no es el propietario del archivo que no se permitirá eliminarlo.

Para el registro, yo uso el archivo de bloqueo para garantizar el acceso exclusivo para el dispositivo conectado al puerto serie (/dev/ttyUSBx de hecho). Asesor método, que requiere la cooperación, está bien. Pero exclusivo, se debe garantizar el acceso entre los diferentes usuarios.

Hay un mejor método de sincronización que el archivo de bloqueo? Cómo determinar si el proceso que creó el archivo de bloqueo todavía se está ejecutando?
Cómo hacer posible que otro usuario para eliminar el archivo de bloqueo si no está en uso?

Una solución que se me ocurrió fue usar el archivo como archivo socket Unix. Si el archivo existe, intente conectar usando el archivo. Si fracasa, se puede asumir que el dueño de proceso de que el archivo está muerto. Esto requiere tener un hilo de bucle en el zócalo accept() en el propietario del proceso. Por desgracia, el sistema no sería atómica más.

  • Parece que nadie ha mencionado la respuesta obvia, que puede o no puede trabajar para usted. POSIX requiere flujo de ops, para ser atómicas, por defecto, y hay un archivo interno de bloqueo para controlar esto. Por lo tanto, si usted puede hacer todo lo que quieras en un solo fprintf, por ejemplo, entonces ya está.
InformationsquelleAutor chmike | 2009-10-21

6 Comentarios

  1. 35

    Echar un vistazo a la iluminación de la presentación Bloqueo de archivo de Trucos y Trampas:

    Esta corta charla presenta varias dificultades comunes de bloqueo de archivos y un par de trucos útiles para utilizar el archivo de bloqueo de forma más efectiva.

    Edición: Para responder a sus preguntas más precisamente:

    Hay un mejor método de sincronización que el archivo de bloqueo?

    Como @Hasturkun ya se ha mencionado y como la presentación anteriormente dicho, la llamada al sistema que usted necesita para utilizar es rebaño(2). Si el recurso que desea compartir entre varios usuarios ya está basada en archivos (en su caso es /dev/ttyUSBx), entonces usted puede flock el archivo de dispositivo en sí.

    Cómo determinar si el proceso que creó el archivo de bloqueo todavía se está ejecutando?

    Usted no tiene que determinar esto, como el flock-ed bloqueo se libera automáticamente al cerrar el descriptor de fichero asociado con su archivo, incluso si el proceso ha finalizado.

    Cómo hacer posible que otro usuario para eliminar el archivo de bloqueo si no está en uso?

    Si desea bloquear el dispositivo de archivo de sí mismo, entonces no habrá necesidad de eliminar el archivo. Incluso si usted decide bloquear un archivo ordinario en /var/lock, con flock usted no tendrá que quitar el archivo para liberar el bloqueo.

    • Me gustaría poder votar tu respuesta de nuevo para los detalles adicionales en
  2. 21

    Probablemente debería ser el uso de flock(), como en

    fd = open(filename, O_RDWR | O_CREAT, 0666); //open or create lockfile
    //check open success...
    rc = flock(fd, LOCK_EX | LOCK_NB); //grab exclusive lock, fail if can't obtain.
    if (rc)
    {
        //fail
    }
    • Buen punto. Tenga en cuenta que debe ser 0666 (octal) y no el 666 y el las banderas o_excl opción también debe ser utilizado. ¿Y el problema de permisos de archivos ? Si otro usuario ha creado el archivo, la convocatoria se producirá incluso si el archivo ya existe y está en uso debido a un acceso de escritura se solicita. Si un acceso de lectura fue solicitada, es posible bloqueo en ella ?
    • Vaya, el las banderas o_excl no debe ser utilizado para permitir la apertura de un archivo existente. A pesar de que el acceso de escritura puede hacer fallar si el archivo es de sólo lectura para el usuario. Flock es independiente de los permisos de los archivos y que este trabajo, incluso si el archivo es de sólo lectura. El umask() la función permite desactivar la máscara de permisos a la hora de crear el archivo.
    • aye, derecho sobre 0666, perezoso mecanógrafo aquí
    • Cuenta que (en Linux) cualquier programa que no llame flock para comprobar si existe un bloqueo será capaz arbitrariamente de lectura/escritura en el archivo, incluso si un bloqueo exclusivo por otro proceso existe.
    • Desafortunadamente, este método no funciona si se ejecuta en sólo lectura del sistema de ficheros y si usted no tiene acceso a cualquier ubicación de escritura.
    • Bueno, es difícil crear un archivo de bloqueo de sólo lectura del sistema de archivos. Usted todavía puede flock() algunos acordado en un archivo de sólo lectura FS pesar de que, tanto tiempo como usted puede open() ella.
    • nope, flock(fd, LOCK_EX|LOCK_NB) NO funciona en modo de sólo lectura del sistema de archivos. Traté de fcntl() así sin suerte. Me gustaría que hubiera estilo de Windows global de exclusión mutua para resolver esto, pero por desgracia, Linux no lo soporta bien.
    • Ese no es el caso. Yo sólo probé a mí mismo, abre el archivo con O_RDONLY, donde el archivo fue de sólo lectura montar sistema de archivos ext4. Se adquirió el bloqueo sin problemas. (un inciso, para su mundial mutexes, ir a leer en semáforos POSIX, a través de sem_open)
    • Me gustaría señalar que lockf() requiere un archivo de escritura, pero flock() no. fcntl() con F_SETLK requiere de una lectura fd para los bloqueos de lectura y una escritura fd para los bloqueos de escritura. también, flock() no funciona a través de NFS (pero fcntl() debería).

  3. 8

    La respuesta de Hasturkun es el que me ha puesto en la pista.

    Aquí es el código que uso

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/file.h>
    #include <fcntl.h>
    
    /*! Try to get lock. Return its file descriptor or -1 if failed.
     *
     *  @param lockName Name of file used as lock (i.e. '/var/lock/myLock').
     *  @return File descriptor of lock file, or -1 if failed.
     */
    int tryGetLock( char const *lockName )
    {
        mode_t m = umask( 0 );
        int fd = open( lockName, O_RDWR|O_CREAT, 0666 );
        umask( m );
        if( fd >= 0 && flock( fd, LOCK_EX | LOCK_NB ) < 0 )
        {
            close( fd );
            fd = -1;
        }
        return fd;
    }
    
    /*! Release the lock obtained with tryGetLock( lockName ).
     *
     *  @param fd File descriptor of lock returned by tryGetLock( lockName ).
     *  @param lockName Name of file used as lock (i.e. '/var/lock/myLock').
     */
    void releaseLock( int fd, char const *lockName )
    {
        if( fd < 0 )
            return;
        remove( lockName );
        close( fd );
    }
    • es probablemente vale la pena el intercambio de ‘quitar’ y ‘cerrar’ en el ‘releaseLock función.
  4. 5

    Tener cuidado con el bloqueo y la liberación de bloqueo de funciones implementadas como se mencionó en una de las respuestas, es decir, como este:

    int tryGetLock( char const *lockName )
    {
        mode_t m = umask( 0 );
        int fd = open( lockName, O_RDWR|O_CREAT, 0666 );
        umask( m );
        if( fd >= 0 && flock( fd, LOCK_EX | LOCK_NB ) < 0 )
        {
            close( fd );
            fd = -1;
        }
        return fd;
    }

    y:

    void releaseLock( int fd, char const *lockName )
    {
        if( fd < 0 )
            return;
        remove( lockName );
        close( fd );
    }

    El problema es que releaseLock a quitar llamar presenta una situación de carrera error. Vamos a considerar que hay tres procesos, todos tratando de adquirir la exclusiva rebaño con desagradables tiempo:

    • Proceso #1 se ha abierto el archivo de bloqueo, adquirió el rebaño, y está a punto de llamar a la función de desbloqueo pero no han hecho eso.
    • Proceso #2 ha llamado a abrir para abrir el archivo apuntado ser lockName, y tiene un descriptor de archivo de la misma, pero que aún no se llama rebaño. Es decir, el archivo apuntado por lockName se abre dos veces ahora.
    • Proceso #3 es que no se ha iniciado todavía.

    Con desagradables tiempo, es posible que el proceso #1 primero las llamadas remove() y close() (no importa el orden) y, a continuación, proceso #2 llamadas al rebaño utilizando el descriptor de archivo abierto ya, pero que ya no está el archivo con el nombre lockName pero un descriptor de archivo que no está vinculada a ninguna entrada de directorio.

    Ahora, si el proceso #3 se inicia la llamada open() crea el lockName archivo, y adquiere el bloqueo sobre él, ya que el archivo no está bloqueado. Como resultado, los procesos de #2 y #3, creo que tanto los propios de la cerradura de nombre de archivo -> un error.

    El problema en la implementación es que eliminar() (o más unlink()) sólo se desvincula el nombre de la entrada de directorio – el archivo descriptor de la referencia a ese archivo es aún utilizable. Uno puede, a continuación, crear otro archivo con el mismo nombre, pero sigue siendo el ya abierto fd se refiere a un lugar diferente.

    Esto puede ser demostrado añadir retardo a la función de bloqueo:

    int tryGetLock( char const *lockName)
    {
        mode_t m = umask( 0 );
        int fd = open( lockName, O_RDWR|O_CREAT, 0666 );
        umask( m );
        printf("Opened the file. Press enter to continue...");
        fgetc(stdin);
        printf("Continuing by acquiring the lock.\n");
        if( fd >= 0 && flock( fd, LOCK_EX | LOCK_NB ) < 0 )
        {
            close( fd );
            fd = -1;
        }
        return fd;
    }
    
    static const char *lockfile = "/tmp/mylock.lock";
    
    int main(int argc, char *argv[0])
    {
        int lock = tryGetLock(lockfile);
        if (lock == -1) {
            printf("Getting lock failed\n");
            return 1;
        }
    
        printf("Acquired the lock. Press enter to release the lock...");
        fgetc(stdin);
    
        printf("Releasing...");
        releaseLock(lock, lockfile);
        printf("Done!\n");
        return 0;

    }

    1. Pruebe a iniciar el proceso #1, y pulse enter una vez para adquirir el bloqueo.
    2. A continuación, inicie el proceso de #2 en otro terminal,
    3. Pulse otra entrar en la terminal, donde el proceso #1 está en marcha para liberar el bloqueo. 4. Continúe con el proceso #2 pulsando el botón enter una vez que se adquiere el bloqueo.
    4. A continuación, abrir otra terminal donde se ejecute el proceso #3. En no, pulse enter una vez para adquirir el bloqueo.

    El «imposible» que sucede: los procesos de #2 y #3 creo que ambos tienen el bloqueo exclusivo.

    Esto puede ser raro que ocurra en la práctica, al menos, con la costumbre de las aplicaciones, pero, sin embargo, la implementación no es correcta.

    También, la creación de un archivo con el modo de 0666 podría ser un riesgo de seguridad.

    No tengo reputación «comentar», y esto también es bastante vieja pregunta, pero la gente se sigue haciendo referencia a esto y hacer algo similar, así que por eso para agregar esta nota como una respuesta.

    • El propósito es proteger contra otro proceso para adquirir el bloqueo. Como usted bien señala, el código no es seguro para subprocesos. Para los subprocesos código, utilice mutex para evitar la condición de carrera.
    • Básicamente, puede dejar fuera de la desvinculación de la lockfile totalmente, ya que introduce un potencial de carrera (y en muchos casos, /tmp, /var/run etc. es probable limpian automáticamente.)
    • Voy a añadir que Nombre de semáforos (como por sem_open) son probablemente los más fáciles de manejar, pero no lockfiles (también Ellos no pueden ser utilizados a través de NFS.). Asimismo, no liberan automáticamente si el proceso se muere. Otra alternativa es el uso de proceso compartido robusto mutexes más de memoria compartida (como estos indican el abandono cuando el propietario termina sin desbloquear)
    • la aplicación no es exactamente correcto con múltiples procesos de cualquiera.
    • Para solucionarlo, como puntero por los demás, sólo pudo salir de la desvinculación de fuera. O tal vez podríamos hacer algo similar a lo que se hace en FreeBSD libutils flopen() aplicación: después de flocado(), fstat() el fd devuelto por abrir y, a continuación, stat() el archivo de bloqueo (con el nombre de archivo de curso) y comprobar que ambas respuestas apuntan al mismo inodo.
    • El efecto de umask es crear el archivo con el permiso de 0. Yo debería haber escrito open( lockName, O_RDWR|O_CREAT, 0 ) para que quede claro. Otro proceso es entonces no se puede abrir el archivo porque de la autorización. Sólo la raíz y el propietario puede eliminar el archivo. El archivo de bloqueo se realiza con el open(). El flock() es para evitar que el mismo proceso se bloquea el archivo más de una vez.

  5. 1

    Yo estaba usando el código publicado por chmike, y notó una pequeña imperfección. He tenido un problema con la carrera, durante la apertura de la cerradura de archivo. A veces, varios hilos de abrir el archivo de bloqueo de forma simultánea.

    Por lo tanto, he quitado la opción de «eliminar( lockName );» línea de «releaseLock()» función. Yo no entiendo por qué, pero de alguna manera esta acción ayudó a la situación.

    He estado usando el siguiente código para probar el bloqueo de archivos. Por su salida se pueden ver cuando varios subprocesos de empezar a utilizar un bloqueo de forma simultánea.

    void testlock(void) {
      # pragma omp parallel num_threads(160)
      {    
        int fd = -1; char ln[] = "testlock.lock";
        while (fd == -1) fd = tryGetLock(ln);
    
        cout << omp_get_thread_num() << ": got the lock!";
        cout << omp_get_thread_num() << ": removing the lock";
    
        releaseLock(fd,ln);
      }
    }
    • El código que me dio no es seguro para subprocesos. Protege proceso diferente para adquirir el bloqueo. Agregar exclusión mutua en las funciones a realizar es seguro para subprocesos.
  6. 1

    Para ampliar Hasturhun la respuesta. En lugar de utilizar la presencia o ausencia de la cerradura de archivo como un indicador, es necesario crear el archivo de bloqueo si dosen no existe y, a continuación, obtener un bloqueo exclusivo en el archivo.

    Las ventajas de este enfoque es que a diferencia de muchos otros métodos de sincronización de los programas, el sistema operativo debería arreglar para usted si su programa se cierra sin desbloquear.

    Por lo que la estructura del programa sería algo así como:

    1: open the lock file creating it if it doesn't exist
    2: ask for an exclusive lock an agreed byte range in the lock file
    3: when the lock is granted then
        4: <do my processing here>
        5: release my lock
        6: close the lock file
    end

    A paso: usted puede bloquear la espera de que el bloqueo sea concedido o regresar de inmediato.
    Los bytes de bloquear en realidad no tienen que existir en el archivo. Si usted puede conseguir el asimiento de una copia de Avanzados de Programación en Unix por Marc J. Rochkind, desarrolla una completa biblioteca de C que utiliza este método para proporcionar una manera de sincronizar los programas que se presenta ordenada por el sistema operativo, sin embargo, los programas de salir.

Dejar respuesta

Please enter your comment!
Please enter your name here