Me acabo de dar cuenta de que puedo mover un programa activo en ejecución a un directorio diferente. En mi experiencia, eso no era posible en MacOs o Windows. ¿Cómo funciona en Ubuntu?
Editar: pensé que no era posible en Mac, pero aparentemente es posible como lo comprueban los comentarios. Tal vez solo no sea posible en Windows. Gracias por todas las respuestas.
rename(2)
ejecutar un ejecutable en OS X? ¿Qué pasa, obtienesEBUSY
o algo? ¿Por qué no funciona? La página del comando man rename (2) no documentaETXTBUSY
esa llamada del sistema, y solo habla sobre laEBUSY
posibilidad de cambiar el nombre del directorio, por lo que no sabía que un sistema POSIX incluso podría rechazar el cambio de nombre de los ejecutables.Respuestas:
Déjame desglosarlo.
Cuando ejecuta un ejecutable, se ejecuta una secuencia de llamadas al sistema, más notablemente
fork()
yexecve()
:fork()
crea un proceso hijo del proceso de llamada, que es (en su mayoría) una copia exacta del padre, ambos ejecutan el mismo ejecutable (usando páginas de memoria de copia en escritura, por lo que es eficiente). Devuelve dos veces: en el padre, devuelve el PID hijo. En el elemento secundario, devuelve 0. Normalmente, el proceso secundario llama a execve de inmediato:execve()
toma una ruta completa al ejecutable como argumento y reemplaza el proceso de llamada con el ejecutable. En este punto, el proceso recién creado obtiene su propio espacio de dirección virtual, es decir, memoria virtual, y la ejecución comienza en su punto de entrada (en un estado especificado por las reglas de la plataforma ABI para procesos nuevos).En este punto, el cargador ELF del kernel ha mapeado los segmentos de texto y datos del ejecutable en la memoria, como si hubiera usado la
mmap()
llamada al sistema (con mapeos de lectura y escritura privados de solo lectura y respectivamente). El BSS también se asigna como con MAP_ANONYMOUS. (Por cierto, estoy ignorando el enlace dinámico aquí por simplicidad: el enlazador dinámicoopen()
s ymmap()
s todas las bibliotecas dinámicas antes de saltar al punto de entrada del ejecutable principal).Solo unas pocas páginas se cargan realmente en la memoria del disco antes de que un ed recién ejecutado () comience a ejecutar su propio código. Se requiere paginación de páginas adicionales según sea necesario, si / cuando el proceso toca esas partes de su espacio de direcciones virtuales. (Precargar cualquier página de código o datos antes de comenzar a ejecutar el código de espacio de usuario es solo una optimización del rendimiento).
El archivo ejecutable se identifica por el inodo en el nivel inferior. Una vez que el archivo ha comenzado a ejecutarse, el núcleo mantiene el contenido del archivo intacto por la referencia del inodo, no por el nombre del archivo, como para los descriptores de archivos abiertos o las asignaciones de memoria respaldadas por archivos. Por lo tanto, puede mover fácilmente el ejecutable a otra ubicación del sistema de archivos o incluso a un sistema de archivos diferente. Como nota al margen, para verificar las diversas estadísticas del proceso, puede echar un vistazo al
/proc/PID
directorio (PID es el ID del proceso del proceso dado). Incluso puede abrir el archivo ejecutable como/proc/PID/exe
, incluso si se ha desvinculado del disco.Ahora profundicemos en el movimiento:
Cuando mueve un archivo dentro de un mismo sistema de archivos, la llamada al sistema que se ejecuta es
rename()
, que simplemente cambia el nombre del archivo a otro nombre, el inodo del archivo sigue siendo el mismo.Mientras que entre dos sistemas de archivos diferentes, suceden dos cosas:
El contenido del archivo se copia primero en la nueva ubicación, por
read()
ywrite()
Después de eso, el archivo se desvincula del directorio de origen usando
unlink()
y obviamente el archivo obtendrá un nuevo inodo en el nuevo sistema de archivos.rm
en realidad solo estáunlink()
haciendo el archivo dado del árbol de directorios, por lo que tener el permiso de escritura en el directorio le dará el derecho suficiente para eliminar cualquier archivo de ese directorio.Ahora, por diversión, ¿qué sucede cuando mueves archivos entre dos archivos y no tienes permiso para
unlink()
el archivo desde el origen?Bueno, el archivo se copiará al destino al principio (
read()
,write()
) y luegounlink()
fallará debido a un permiso insuficiente. ¡Entonces, el archivo permanecerá en ambos sistemas de archivos!fuente
mmap
yunmap
sistema de llamadas no se utilizan para cargar y descargar las páginas de la demanda, las páginas se cargan por el núcleo cuando se accede a ellas generan un fallo de página, las páginas se descargan de la memoria cuando el sistema operativo se siente la RAM sería mejor utilizar para otra cosa. Ninguna llamada al sistema está involucrada en estas operaciones de carga / descarga.Bueno, eso es bastante sencillo. Tomemos un ejecutable llamado / usr / local / bin / whoopdeedoo. Eso es solo una referencia al llamado inodo (estructura básica de archivos en Unix Filesystems). Es el inodo que se marca "en uso".
Ahora, cuando elimina o mueve el archivo / usr / local / whoopdeedoo, lo único que se mueve (o borra) es la referencia al inodo. El inodo en sí permanece sin cambios. Eso es básicamente todo.
Debería verificarlo, pero creo que también puedes hacerlo en sistemas de archivos Mac OS X
Windows toma un enfoque diferente. ¿Por qué? Quién sabe...? No estoy familiarizado con los aspectos internos de NTFS. Teóricamente, todos los sistemas de archivos que usan referencias a estructuras internas para nombres de archivos deberían poder hacer esto.
Admito que simplifiqué demasiado, pero ve a leer la sección "Implicaciones" en Wikipedia, que hace un trabajo mucho mejor que yo.
fuente
Una cosa que parece faltar en todas las otras respuestas es que: una vez que se abre un archivo y un programa contiene un descriptor de archivo abierto, el archivo no se eliminará del sistema hasta que se cierre ese descriptor de archivo.
Los intentos de eliminar el inodo referenciado se retrasarán hasta que se cierre el archivo: el cambio de nombre en el mismo sistema de archivos o uno diferente no puede afectar el archivo abierto, independientemente del comportamiento del cambio de nombre, ni eliminar o sobrescribir explícitamente el archivo con uno nuevo. La única forma en que puede desordenar un archivo es abriendo explícitamente su inodo y desordenando el contenido, no mediante operaciones en el directorio, como renombrar / eliminar el archivo.
Además, cuando el kernel ejecuta un archivo, mantiene una referencia al archivo ejecutable y esto evitará nuevamente cualquier modificación durante la ejecución.
Entonces, al final, incluso si parece que puede eliminar / mover los archivos que componen un programa en ejecución, en realidad el contenido de esos archivos se mantiene en la memoria hasta que finaliza el programa.
fuente
execve()
no devuelve ningún FD, simplemente ejecuta el programa. Así, por ejemplo, si se ejecutatail -f /foo.log
a continuación, su es un FD (/proc/PID/fd/<fd_num>
) asociado contail
elfoo.log
pero no para el propio ejecutable,tail
y no en su matriz también. Esto también es cierto para los ejecutables individuales.execve
así que no veo cómo esto es relevante. Una vez que el kernel comienza a ejecutar un archivo, intentar reemplazar el archivo no modificará el programa que el kernel va a cargar, lo que hace que el punto sea discutible. Si desea "actualizar" el ejecutable mientras se está ejecutando, puede llamarexecve
en algún momento para que el núcleo vuelva a leer el archivo, pero no veo cómo esto importa. El punto es: eliminar un "ejecutable en ejecución" realmente no desencadena ninguna eliminación de datos hasta que se detiene el ejecutable.execve()
y un FD cuando no hay FD involucrado en este caso.open()
devuelve un descriptor de archivo , del que habla Heemayl aquíexecve()
. Sí, un proceso en ejecución tiene una referencia a su ejecutable, pero ese no es un descriptor de archivo. Probablemente, incluso simunmap()
editara todas sus asignaciones de su ejecutable, todavía tendría una referencia (reflejada en / proc / self / exe) que impedía que se liberara el inodo. (Esto sería posible sin fallar si lo hiciera desde una función de biblioteca que nunca regresó). Por cierto, truncar o modificar un ejecutable en uso podría darleETXTBUSY
, pero podría funcionar.En un sistema de archivos de Linux, cuando mueve un archivo, siempre que no cruce los límites del sistema de archivos (léase: permanece en el mismo disco / partición), todo lo que está cambiando es el inodo de
..
(directorio padre) al de la nueva ubicación . Los datos reales no se han movido en absoluto en el disco, solo el puntero para que el sistema de archivos sepa dónde encontrarlos.Esta es la razón por la cual las operaciones de movimiento son tan rápidas y es probable que no haya ningún problema en mover un programa en ejecución, ya que en realidad no está moviendo el programa en sí.
fuente
Es posible porque mover un programa no afecta los procesos en ejecución iniciados al iniciarlo.
Una vez que se inicia un programa, sus bits en el disco están protegidos para que no se sobrescriban, pero no es necesario proteger el archivo para cambiarle el nombre, moverlo a una ubicación diferente en el mismo sistema de archivos, lo que equivale a cambiar el nombre del archivo o moverlo a un sistema de archivos diferente, que es equivalente a copiar el archivo en otro lugar y luego eliminarlo.
Al eliminar un archivo que está en uso, ya sea porque un proceso tiene un descriptor de archivo abierto o porque un proceso lo está ejecutando, no elimina los datos del archivo, que permanece referenciado por el inodo del archivo, sino que solo elimina la entrada del directorio, es decir, una ruta desde la cual se puede alcanzar el inodo.
Tenga en cuenta que iniciar un programa no carga todo de una vez en la memoria (física). Por el contrario, solo se carga el mínimo estricto requerido para que comience el proceso. Luego, las páginas requeridas se cargan a pedido durante toda la vida del proceso. Esto se llama paginación de demanda. Si hay escasez de RAM, el sistema operativo es libre de liberar la RAM que contiene estas páginas, por lo que es muy posible que un proceso cargue varias veces la misma página desde el inodo ejecutable.
La razón por la que no fue posible con Windows originalmente se debe probablemente al hecho de que el sistema de archivos subyacente (FAT) no admitía el concepto dividido de entradas de directorio frente a inodos. Esta limitación ya no estaba presente con NTFS, pero el diseño del sistema operativo se ha mantenido durante mucho tiempo, lo que lleva a la restricción desagradable de tener que reiniciar al instalar una nueva versión de un binario, que ya no es el caso con las versiones recientes de Windows.
fuente
Básicamente, en Unix y sus características, se usa un nombre de archivo (incluida la ruta del directorio que lo conduce) para asociar / encontrar un archivo al abrirlo (ejecutar un archivo es una forma de abrirlo de alguna manera). Después de ese momento, la identidad del archivo (a través de su "inodo") se establece y ya no se cuestiona. Puede eliminar el archivo, cambiarle el nombre, cambiar sus permisos. Siempre que un proceso o una ruta de archivo tenga un identificador en ese archivo / inodo, se mantendrá, al igual que una tubería entre procesos (en realidad, en UNIX histórico una tubería era un inodo sin nombre con un tamaño que simplemente encajaba en el referencia de almacenamiento en disco de "bloques directos" en el inodo, algo así como 10 bloques).
Si tiene un visor de PDF abierto en un archivo PDF, puede eliminar ese archivo y abrir uno nuevo con el mismo nombre, y mientras el antiguo visor esté abierto, seguirá accediendo al archivo antiguo (a menos que lo vea activamente el sistema de archivos para notar cuándo desaparece el archivo con su nombre original).
Los programas que necesitan archivos temporales solo pueden abrir dicho archivo con algún nombre y luego inmediatamente eliminarlo (o más bien su entrada de directorio) mientras aún está abierto. Posteriormente, el archivo ya no es accesible por nombre, pero cualquier proceso que tenga un descriptor de archivo abierto para el archivo aún puede acceder a él, y si después hay una salida inesperada del programa, el archivo se eliminará y el almacenamiento se recuperará automáticamente.
Por lo tanto, la ruta a un archivo no es una propiedad del archivo en sí (de hecho, los enlaces duros pueden proporcionar varias rutas diferentes) y solo se necesita para abrirlo, no para el acceso continuo de los procesos que ya lo tienen abierto.
fuente