¿Son atómicos los comandos encadenados?

8

Si hubiera un proceso escribiendo continuamente en un archivo, y quisiera tomar el control del archivo con root, podría hacer algo como esto:

sudo rm somefile; sudo touch somefile

¿Es posible que el proceso de adición anexe al archivo entre estos dos comandos? Si es así, ¿hay alguna manera de garantizar que no se ejecute ningún otro comando en el medio?

Darth Egregious
fuente
44
Esto podría ser un problema XY . ¿Puedes explicar el problema subyacente que estás tratando de resolver?
Nate Eldredge
1
@NateEldredge: Totalmente no. Puedo entender por qué parece así basado en mi hipótesis hipotética. Ni siquiera tengo un problema. Sólo curioso.
Darth Egregious
55
El proceso probablemente continuará escribiendo en el archivo eliminado y ni siquiera intentará abrir el nuevo.
Random832
Mi suposición en la hipótesis resultó ser un poco errónea, pero supongo que podría cambiar "agregar" a "abrir" o "tocar" y aún tendría sentido.
Darth Egregious

Respuestas:

12

Una línea de comando encadenada es básicamente un pequeño script de shell; ejecutará el primer comando utilizando el procedimiento habitual de fork + exec, esperará a que salga y luego ejecutará el segundo de la misma manera. Entre los dos comandos, hay una cantidad arbitraria de tiempo que el shell tarda en su contabilidad y procesamiento, durante el cual tiene lugar el multiprocesamiento ordinario y otros procesos arbitrarios pueden hacer otras cosas arbitrarias. Por tanto, la respuesta es no'. (Si realmente hace esto, encontrará que la entrada del directorio para somefiledesaparece, pero el archivo en sí permanece (ya que es abierto por un proceso) hasta que se cierra. El espacio en disco utilizado por el archivo no será reclamado hasta que eso suceda. Mientras tanto , el touchcomando creará un nuevo archivo no relacionado con el mismo nombre y ruta).

Si desea cambiar la propiedad del archivo a raíz, simplemente hágalo sudo chown root:root somefile(aunque no estoy seguro de cómo eso afectará los procesos con un identificador de archivo abierto). Si desea destruir el contenido del archivo actual, intente truncate -s 0 somefile(el proceso en ejecución continuará anexándose al archivo ahora vacío). Si se trata de algo más, quizás aclare lo que quiere hacer.

Tom Hunt
fuente
Gracias Tom. No estaba tan preocupado por mi situación hipotética. Me preguntaba si hacer algo como esto es posible.
Darth Egregious
Con respecto a la pregunta general, no creo que haya realmente una manera de garantizar la atomicidad en un script de shell. Puede usar archivos de bloqueo y demás para asegurarse de que dos procesos cooperantes no se crucen entre sí, y puede usar permisos para asegurarse de que un proceso arbitrario no pueda pisar su trabajo. Pero evitar que algo más se ejecute afectaría mucho al multiprocesamiento del kernel; No creo que el código ring-3 pueda hacer eso en absoluto.
Tom Hunt
Puede usar bloqueos de archivos obligatorios si están habilitados y disponibles en su sistema de archivos.
roaima
55
chowning somefile no afectará los procesos que tienen un identificador de archivo para somefile.
PSkocik
Por ejemplo, puedes hacer exec 3>somefile; sudo chmod 0600 somefile; echo hello world >&3; sudo cat somefile; exec 3>&-.
PSkocik
6

No. Un rmcomando seguido de un touchcomando no es atómico en absoluto. Pasará mucho tiempo entre los dos comandos, posiblemente en el rango de milisegundos. Pueden pasar muchas cosas durante ese tiempo. Si no tienes suerte, tus credenciales de sudo podrían incluso caducar.

Un solo programa que llama unlinky openllama dejaría una ventana mucho más corta para una carrera, pero aún podría suceder.

Un enfoque más seguro sería crear el nuevo archivo con un nombre temporal y usar la renamellamada al sistema. "Sobrescribir" un nombre con la renamellamada del sistema se garantiza que sea atómico. Esto podría lograrse usando touchy mv.

Pero un proceso que ha abierto el archivo antiguo para escritura puede seguir escribiendo mucho después de que se haya eliminado. Este será el caso tanto para un archivo eliminado usando unlinkcomo para uno eliminado rename.

kasperd
fuente
3

Una vez que un proceso tiene un identificador de archivo abierto para leer, no importa lo que haga con la propiedad o los permisos: el proceso puede continuar accediendo al archivo. Incluso puede eliminar el archivo y el proceso podrá continuar accediendo a él a través del controlador de archivos.

Considere los permisos como una puerta de control para obtener un identificador de archivo.

Si desea crear un archivo atómicamente, en lugar de hacerlo:

sudo rm somefile
sudo touch somefile

podrías considerar esto:

sudo touch anotherfile
sudo perl -e "rename 'anotherfile', 'somefile'"

que reemplazará atómicamente somefilecon anotherfile. (Hubiera preferido usar, mv -fpero no puedo encontrar una declaración que garantice que esto llame a la rename(2)llamada del sistema.


Quizás pueda actualizar su pregunta para explicar lo que quiere decir con "tomar el control del archivo". Puede tener un problema XY aquí.

roaima
fuente
Use mv -fpara obtener una rename(2)llamada al sistema. Tienes razón en que lnno, porque siempre usa la link(2)llamada al sistema, que no puede reemplazar atómicamente. ln -fsolo lo hace unlink(2)en el objetivo primero.
Peter Cordes
@ PeterCordes ah sí, por supuesto, gracias. Tenía lnen el cerebro. No estaba seguro de cómo obtener la rename(2)garantía de estar involucrado y era demasiado tarde para seguir cavando
roaima
POSIX garantiza que mv"realizará acciones equivalentes a" rename(old, new). pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html . mv -factúa como mv -isi el destino no se puede escribir, pero no es como cp -fo ln -fdónde se desenlazará primero.
Peter Cordes
2

No pero

sudo rm somefile; sudo touch somefile

Es seguro en la mayoría de las situaciones.

La mayoría de los procesos abren archivos y luego usan el descriptor de archivo adquirido para acceder al contenido del archivo.

Si un proceso abre algún archivo, en sh:

 exec 3>somefile

Luego, otro proceso es libre de desvincular (= eliminar) algún archivo y el descriptor de archivo en el que se abrió algún archivo mediante el primer proceso (3 en este caso) continuará haciendo referencia al contenido original de algún archivo, que ahora es un archivo en el limbo.

sudo touch somefile

creará un archivo nuevo y no relacionado y todos los procesos que tenían abierto el archivo anterior y ahora solo están usando un descriptor de archivo para referirse a él no se verán afectados porque se refieren a un archivo diferente, uno que ahora está en el limbo.

Si un proceso no root intenta hacer referencia a algún archivo por su nombre, obtendrán un error EPERM, porque el nuevo archivo es propiedad de root.

Si desea evitar que múltiples procesos bajo el mismo usuario (por ejemplo, root) dañen un archivo, Linux tiene un bloqueo de archivos obligatorio y de aviso. Puede usar el flockcomando en los scripts de shell para bloquear el archivo de aviso (consulte la página de manual para obtener más información).

PSkocik
fuente