linux - varios - Mover un directorio de forma atómica
mover varios archivos en linux (13)
Tengo dos directorios en el mismo directorio principal. Llame a la base del directorio principal y a los directorios hijos alfa y bravo . Quiero reemplazar alfa con bravo . El método más simple es:
rm -rf alpha
mv bravo alpha
El comando mv es atómico, pero el rm -rf no lo es. ¿Hay una manera simple en bash de reemplazar atómicamente alfa con bravo ? Si no, ¿hay una forma complicada?
APÉNDICE:
Por cierto, no es un problema insuperable si el directorio no existe por un corto período. Solo hay un lugar que intenta acceder a alpha, y comprueba si alpha existe antes de hacer algo crítico. Si no, da un mensaje de error. Pero sería bueno si hubiera una manera de hacer esto. :) Tal vez hay alguna forma de modificar los inodos directamente, o algo ...
¿Por qué no haces algo como esto?
rm -rf alpha/*
mv bravo/* alpha/
rm -rf bravo/
Eso significaría que todo en alfa se destruye, alfa nunca se elimina, y todos los contenidos se mueven.
Algo a tener en cuenta es que si su proceso tiene alguno de los archivos en alfa abierto cuando se produce este movimiento / eliminación, el proceso no se notará y los datos escritos se perderán cuando el archivo se cierre y finalmente se elimine.
Incluso si estuviese accediendo a los inodos directamente, todavía no habría forma de intercambiar atómicamente los valores de inodos en el espacio de usuario.
No creo que haya una forma atómica de hacer esto. Tu mejor opción es hacer algo como esto:
mv alpha delme
mv bravo alpha
rm -rf delme
Preocuparse por la naturaleza atómica de la operación no tiene sentido. El hecho es que el acceso a alfa por la otra tarea no será atómico de todos modos.
El enfoque semáforo de Oddthinking es el único camino a seguir.
Si no puede modificar la otra tarea, deberá asegurarse de que no se está ejecutando antes de realizar la sustitución.
Si te refieres a atómico en ambas operaciones, no lo creo. Lo más cercano sería:
mv alpha delta
mv bravo alpha
rm -rf delta
pero eso aún tendría una pequeña ventana donde alfa no existía.
Para minimizar la probabilidad de que algo intente usar alfa mientras no está allí, podrías (si tienes autoridad):
nice --20 ( mv alpha delta ; mv bravo alpha )
rm -rf delta
lo cual aumentará sustancialmente la prioridad de su proceso mientras las operaciones de mv
están sucediendo.
Si, como dices en tu apéndice, solo hay un lugar que verifica los errores alfa y sus errores, si no está allí, puedes cambiar el código para que no se produzca un error inmediatamente, pero vuelve a intentarlo en poco tiempo (fácilmente por segundo para dos operaciones mv
) - estos reintentos deberían aliviar cualquier problema a menos que reemplaces alfa con mucha frecuencia.
Utilice una operación atómica garantizada por separado para actuar como un semáforo.
Entonces, si la creación y eliminación de un archivo de operaciones son atómicas:
1) crea un archivo llamado "semáforo".
2) Si y solo si eso es exitoso (no hay conflicto con el archivo existente), realice la operación (ya sea procesando alfa o moviendo el directorio, dependiendo del proceso)
3) semáforo rm.
La sección de documentación de SQLite Bloqueo de archivos y concurrencia en SQLite versión 3 tiene una descripción bien escrita de su protocolo de bloqueo progresivo para controlar la lectura concurrente, la escritura exclusiva y la reversión después de un bloqueo. Algunas de esas ideas se aplican aquí.
mv y ln se pueden usar para operaciones atómicas. He usado ln (1) para implementar aplicaciones web de forma atómica.
La forma correcta de reemplazar un enlace simbólico es con ln -nsf
ln -nsf <target> <link_name>
p.ej
$ mkdir dir1
$ mkdir dir2
$ ln -s dir1 mylink
$ ls -l mylink
lrwxrwxrwx 1 phil phil 4 Nov 16 14:45 mylink -> dir1
$ ln -nsf dir2 mylink
$ ls -l mylink
lrwxrwxrwx 1 phil phil 4 Nov 16 14:46 mylink -> dir2
Puedes hacer esto si usas enlaces simbólicos:
Digamos que alfa es un enlace simbólico al directorio alpha_1, y desea cambiar el enlace simbólico para apuntar a alpha_2. Esto es lo que parece antes del cambio:
$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Para hacer que alfa se refiera a alpha_2, use ln -nsf:
$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2
Ahora puedes eliminar el directorio anterior:
$ rm -rf alpha_1
Tenga en cuenta que esto NO es realmente una operación totalmente atómica, pero ocurre muy rápidamente ya que el comando "ln" desvincula ambos y luego recrea inmediatamente el enlace simbólico. Puedes verificar este comportamiento con strace:
$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...
Puede repetir este procedimiento como desee: por ejemplo, cuando tiene una nueva versión, alpha_3:
$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2
La solución final es combinar el enfoque simbólico y el cambio de nombre:
mkdir alpha_real
ln -s alpha_real alpha
# now use "alpha"
mkdir beta_real
ln -s beta_real tmp
# atomically rename "tmp" to "alpha"
# use -T to actually replace "alpha" instead of moving *into* "alpha"
mv -T tmp alpha
Por supuesto, la aplicación que accede a alfa tiene que ser capaz de tratar con enlaces simbólicos que cambian en la ruta.
Retomando la solución de David aquí, que es totalmente atómica ... el único problema con el que se encontraría es que la opción -T
para mv
no es POSIX, por lo que ciertos sistemas operativos POSIX pueden no ser compatibles (FreeBSD, Solaris, etc. . ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html ). Con una ligera modificación, este enfoque puede modificarse para que sea totalmente atómico y portátil en todos los sistemas operativos POSIX:
mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./
exaple via: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/
Esto debería funcionar:
mkdir bravo_dir alpha_dir
ln -s bravo_dir bravo
ln -s alpha_dir alpha
mv -fT bravo alpha
strace mv -fT bravo alpha muestra:
rename("bravo", "alpha")
que me parece bastante atómico.