2008-11-21 13 views
27

Tengo dos directorios en el mismo directorio principal. Llame al directorio principal base y los directorios hijos alpha y bravo. Quiero reemplazar alpha con bravo. El método más simple es:Mover un directorio atómicamente

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 alpha con bravo? Si no, ¿hay una forma complicada?

Adición:

Por cierto, no es un problema insuperable si el directorio no existe para un período corto. 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 manera de modificar los inodes directamente, o algo ...

+3

Su prueba en la adición no es seguro - no es una condición de carrera. Considere lo que sucede si la comprobación se ejecuta primero (y existe Alpha) y luego se desactiva mientras que el segundo proceso elimina Alpha, y luego se vuelve a encender para continuar la ejecución, con Alpha ahora faltante. – Oddthinking

Respuesta

14

Usted puede hacer esto si utiliza enlaces simbólicos:

Digamos alfa es un enlace simbólico a alpha_1 directorio y desea cambiar el enlace simbólico para apuntar a alpha_2.Esto es lo que se ve como antes de la conmutación:

$ ls -l 
lrwxrwxrwx alpha -> alpha_1 
drwxr-xr-x alpha_1 
drwxr-xr-x alpha_2 

Para hacer alfa se refiere a alpha_2, -nsf uso ln:

$ ln -nsf alpha_2 alpha 
$ ls -l 
lrwxrwxrwx alpha -> alpha_2 
drwxr-xr-x alpha_1 
drwxr-xr-x alpha_2 

ya se puede quitar 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. Puede 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: p. cuando tiene una nueva versión, alpha_3:

$ ln -nsf alpha_3 alpha 
$ rm -rf alpha_2 
+0

Linux VFS no es compatible con enlaces duros de directorios múltiples. Algunos otros * nixes tienen soporte limitado, restringido al superusuario. También tendría que recopilar todos los enlaces ahora huérfanos de los subdirectorios y archivos. – JimB

+0

Sí, debe ser un enlace suave para ser de aplicación general. He editado mi respuesta. Sin embargo, no creo que haya ningún huérfano, siempre y cuando alfa siempre sea un enlace, que es lo que quise decir al cambiar la pregunta ligeramente. Por supuesto, siempre tendrá que eliminar la versión anterior del dir –

+0

Muy cerca; Resulta que también necesita la opción -n, de lo contrario terminará creando un enlace simbólico en el directorio original. De hecho, probé tu idea antes de publicar la pregunta, y no funcionó, pero cuando volví a mirar y noté la bandera -n, eso lo hizo. También caca en quien lo votó :) – dirtside

0

No creo que haya ninguna forma atómica de hacer esto. Su mejor apuesta es hacer algo como esto:

mv alpha delme 
mv bravo alpha 
rm -rf delme 
6

Si se refiere atómica a través de ambas operaciones, no lo creo. El más cercano sería:

mv alpha delta 
mv bravo alpha 
rm -rf delta 

pero que todavía tendría una pequeña ventana en la que no existía alfa.

Para reducir al mínimo la probabilidad de que cualquier cosa que trate de usar alfa, mientras que no está ahí que podría (si usted tiene la autoridad):

nice --20 (mv alpha delta ; mv bravo alpha) 
rm -rf delta 

que poner encima de tu prioridad del proceso considerablemente mientras que los mv operaciones están sucediendo.

Si, como dice en su apéndice, solo hay un lugar que comprueba alfa y si hay errores, puede cambiar el código para que no se produzca un error de inmediato, pero vuelva a intentarlo en poco tiempo (fácilmente por debajo del segundo para dos operaciones mv): estos intentos deberían aliviar cualquier problema a menos que reemplace con frecuencia .

+0

Eso es lo más rápido que puedes hacerlo en el caparazón; podría escribir una pieza personalizada de C para mover los dos directorios que eliminarían algunos milisegundos del intervalo de tiempo, o usar una secuencia de comandos de Perl (o elegir su propio veneno). Sin embargo, no tendría sentido reescribir 'rm -fr'. –

1

Incluso si estuviese accediendo a los inodos directamente, aún no habría forma de intercambiar atómicamente los valores de inodos en el espacio de usuario.

6

Utilice una operación atómica garantizada para actuar como semáforo.

lo tanto, si la creación y la eliminación de un archivo de operaciones son atómicos:

1) crear un archivo llamado "semáforo".

2) si y sólo si es exitosa (sin conflictos con el archivo existente), hacer la operación (cualquiera de los procesos alfa o mover el directorio, dependiendo del proceso)

3) rm semáforo.

+0

que solo ayudaría si se vuelve a escribir alguna operación que supuestamente ocurriera en alfa para verificar primero por 'semáforo' y esperar para poder bloquear el semáforo en sí mismo ... y si le impiden crear su semáforo cuando comienzan su propio operación. – PypeBros

+0

@PypeBros: Sí. Si no lo comprueba antes de realizar la operación, no se está utilizando como semáforo.Si puede crearse mediante dos procesos concurrentes, no es un semáforo. – Oddthinking

0

Algo a tener en cuenta es que si su proceso tiene alguno de los archivos en alfa abierto cuando ocurre 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.

1

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, entonces deberá asegurarse de que no esté funcionando antes de realizar la sustitución.

-2

¿Por qué no simplemente hacer algo como:

rm -rf alpha/* 
mv bravo/* alpha/ 
rm -rf bravo/ 

Eso significaría que todo en alfa se destruye, alfa no se elimina, y todo el contenido se desplace de.

4

La sección de documentación SQLiteFile Locking and Concurrency in SQLite Version 3 tiene una descripción bien escrita de su protocolo de bloqueo progresivo para controlar la lectura simultánea, escritura exclusiva y reversión después de un bloqueo. Algunas de esas ideas se aplican aquí.

0

mv y ln se pueden utilizar para operaciones atómicas. He usado ln (1) para implementar aplicaciones web de forma atómica.

La forma correcta para sustituir un enlace simbólico es con -nsf ln

ln -nsf <target> <link_name> 

por ejemplo

$ 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 
+3

ln -nsf es rápido pero en realidad no es atómico. El proceso ln realmente se desvincula y luego recrea inmediatamente el enlace simbólico. Puedes verificar esto con strace. Ver http://blog.moertel.com/articles/2005/08/22/how-to-change-symlinks-atomically –

35

la solución final es la combinación de los symlink- y el cambio de nombre-enfoque:

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 alfa acceder a la aplicación tiene que ser capaz de tratar con los enlaces simbólicos cambiantes en la ruta.

+4

http://rcrowley.org/2010/01/06/things-unix-can-do -atomically.html para una explicación de la bandera '-T' de' mv' que permite el intercambio de dos enlaces simbólicos de forma atómica. – PypeBros

5

Recogiendo solución de David aquí, que es totalmente atómica ... el único problema que había corrido hacia es que la opción de -Tmv no es POSIX, y así ciertos sistemas operativos POSIX no sea compatible con él (FreeBSD, Solaris, etc. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). Con una ligera modificación, este enfoque puede ser alterado para ser plenamente atómica, y portable a través de 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 vía: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

3

Esto debería hacer el truco:

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 se ve bastante atómico t o yo

0

También es posible sustituir un todo partes del contenido a la vez en algún prefijo (Z aquí) usando unionfs-fuse:

# mkdir a b c Z 
# touch a/1 b/2 c/3 
# ln -s a X 
# ln -s b Y 
# unionfs X=RW:Y=RW Z 
# shopt -s globstar 
# file ** 
a: directory 
a/1: empty 
b: directory 
b/2: empty 
c: directory 
c/3: empty 
X: symbolic link to a 
Y: symbolic link to b 
Z: directory 
Z/1: empty 
Z/2: empty 
# ln -sfn c Y 
# file **/* 
a: directory 
a/1: empty 
b: directory 
b/2: empty 
c: directory 
c/3: empty 
X: symbolic link to a 
X/1: empty 
Y: symbolic link to c 
Y/3: empty 
Z: directory 
Z/1: empty 
Z/3: empty 
# fusermount -u Z 
# rm -r a b c X Y Z 
Cuestiones relacionadas