c flock

flock(): eliminando el archivo bloqueado sin condición de carrera



(3)

  1. En Unix, es posible eliminar un archivo mientras está abierto: el archivo se mantendrá hasta que todos los procesos hayan finalizado y lo tengan en su lista de descriptores de archivos.
  2. en Unix es posible verificar que un archivo se haya eliminado de todos los directorios comprobando el recuento de enlaces a medida que se convierte en cero

Entonces, en lugar de comparar el valor ino de las rutas de archivos antiguas / nuevas, simplemente puede verificar el recuento nlink en el archivo que ya está abierto. Se supone que es solo un archivo de bloqueo efímero y no un recurso / dispositivo de exclusión mutua real.

lockfile = "/tmp/some_name.lock"; for(int attempt; attempt < timeout; ++attempt) { int fd = open(lockfile, O_CREAT); int done = flock(fd, LOCK_EX | LOCK_NB); if (done != 0) { close(fd); sleep(1); // lock held by another proc continue; } struct stat st0; fstat(fd, &st0); if(st0.st_nlink == 0) { close(fd); // lockfile deleted, create a new one continue; } do_something(); unlink(lockfile); // nlink :=0 before releasing the lock flock(fd, LOCK_UN); close(fd); // release the ino if no other proc return true; } return false;

Estoy usando flock () para mutexes con nombre entre procesos (es decir, algún proceso puede decidir mantener un bloqueo en "some_name", que se implementa al bloquear un archivo llamado "some_name" en un directorio temporal:

lockfile = "/tmp/some_name.lock"; fd = open(lockfile, O_CREAT); flock(fd, LOCK_EX); do_something(); unlink(lockfile); flock(fd, LOCK_UN);

El archivo de bloqueo debe eliminarse en algún momento, para evitar llenar el directorio temporal con cientos de archivos.

Sin embargo, hay una condición de carrera obvia en este código; Ejemplo con procesos A, B y C:

A opens file A locks file B opens file A unlinks file A unlocks file B locks file (B holds a lock on the deleted file) C opens file (a new file one is created) C locks file (two processes hold the same named mutex !)

¿Hay alguna forma de eliminar el archivo de bloqueo en algún momento sin introducir esta condición de carrera?


Lo siento si respondo a una pregunta muerta:

Después de bloquear el archivo, abra otra copia, fstat ambas copias y verifique el número de inodo, como este:

lockfile = "/tmp/some_name.lock"; while(1) { fd = open(lockfile, O_CREAT); flock(fd, LOCK_EX); fstat(fd, &st0); stat(lockfile, &st1); if(st0.st_ino == st1.st_ino) break; close(fd); } do_something(); unlink(lockfile); flock(fd, LOCK_UN);

Esto evita la condición de carrera, porque si un programa mantiene un bloqueo en un archivo que todavía está en el sistema de archivos, cualquier otro programa que tenga un archivo sobrante tendrá un número de inodo incorrecto.

De hecho, lo probé en el modelo de máquina de estado, usando las siguientes propiedades:

Si P_i tiene un descriptor bloqueado en el sistema de archivos, entonces no hay otro proceso en la sección crítica.

Si P_i está detrás de la estadística con el inodo derecho o en la sección crítica, tiene el descriptor bloqueado en el sistema de archivos.


Si utiliza estos archivos solo para el bloqueo y no los escribe, sugiero que trate la existencia de la entrada del directorio como una indicación de un bloqueo retenido y evite utilizar el flock completo.

Para hacerlo, debe construir una operación que cree una entrada de directorio e informe de un error si ya existía. En Linux y con la mayoría de los sistemas de archivos, pasar O_EXCL para open funcionará para esto. Pero algunas plataformas y algunos sistemas de archivos (NFS más antiguo en particular) no son compatibles con esto. La open sugiere una alternativa:

Los programas portátiles que desean realizar un bloqueo atómico de archivos utilizando un archivo de bloqueo, y deben evitar la dependencia en el soporte de NFS para O_EXCL , pueden crear un archivo único en el mismo sistema de archivos (por ejemplo, incorporando nombre de host y PID), y usar el link (2) para Hacer un enlace al archivo de bloqueo. Si el link (2) devuelve 0, el bloqueo es exitoso. De lo contrario, use stat (2) en el archivo único para verificar si su conteo de enlaces ha aumentado a 2, en cuyo caso el bloqueo también es exitoso.

Entonces, esto parece un esquema de bloqueo que está documentado oficialmente y, por lo tanto, indica un cierto nivel de apoyo y sugerencias de mejores prácticas. Pero también he visto otros enfoques. bzr por ejemplo, utiliza directorios en lugar de enlaces simbólicos en la mayoría de los lugares. Citando de su código fuente :

Un bloqueo está representado en el disco por un directorio de un nombre particular, que contiene un archivo de información. El bloqueo se realiza cambiando el nombre de un directorio temporal en su lugar. Usamos directorios temporales porque, para todos los transportes y sistemas de archivos conocidos, creemos que exactamente un intento de reclamar el bloqueo tendrá éxito y los otros fallarán. (Los archivos no funcionarán porque algunos sistemas de archivos o transportes solo tienen renombrar y sobrescribir, lo que dificulta saber quién ganó).

Una desventaja de los enfoques anteriores es que no se bloquearán: un intento fallido de bloqueo resultará en un error, pero no espere hasta que el bloqueo esté disponible. Tendrá que sondear el bloqueo, lo que podría ser problemático a la luz de la contención del bloqueo. En ese caso, es posible que desee alejarse más de su enfoque basado en el sistema de archivos y usar implementaciones de terceros en su lugar. Pero ya se han formulado preguntas generales sobre cómo hacer mutex de ipc, por lo que le sugiero que busque [ipc] [mutex] y eche un vistazo a los resultados, este en particular. Por cierto, estas etiquetas también pueden ser útiles para tu publicación.