¿Qué genera el mensaje "archivo de texto ocupado" en Unix?

137

¿Qué operación genera el error "archivo de texto ocupado"? No puedo decir exactamente.

Creo que está relacionado con el hecho de que estoy creando un script temporal de Python (usando tempfile) y usando execl a partir de él, pero creo que execl cambia el archivo que se está ejecutando.

Penz
fuente

Respuestas:

130

Este error significa que algún otro proceso o usuario está accediendo a su archivo. Use lsofpara verificar qué otros procesos lo están usando. Puede usar el killcomando para matarlo si es necesario.

jaypal singh
fuente
115
El Text file busyerror en específico se trata de intentar modificar un ejecutable mientras se está ejecutando. El "Texto" aquí se refiere al hecho de que el archivo que se está modificando es el segmento de texto para un programa en ejecución. Este es un caso muy especial, y no el genérico que su respuesta parece sugerir. Aun así, su respuesta no es del todo incorrecta.
ArjunShankar
44
La respuesta con el comentario parece completa.
Penz
El OP preguntó qué operación genera el error, no para una explicación de lo que significa el error.
WonderWorker
Creo que el hecho de que Unix asume que los archivos son "archivos de texto" es ilógico, en mi caso fue un archivo binario que provocó este error.
Felipe Valdés
1
@FelipeValdes El nombre es histórico de la terminología de hace medio siglo. Por ejemplo, en multics, el segmento de texto de un programa era distinto del segmento de enlace, e incluso la gente anterior hablaba de texto binario. stackoverflow.com/a/1282540/833300
jma
30

Ha pasado un tiempo desde que vi ese mensaje, pero solía prevalecer en el Sistema V R3 o hace un par de décadas. En aquel entonces, significaba que no podía cambiar el ejecutable de un programa mientras se estaba ejecutando.

Por ejemplo, estaba construyendo un maketrabajo llamado rmk, y después de un tiempo fue autosuficiente. Ejecutaría la versión de desarrollo y haría que construyera una nueva versión. Para que funcione, fue necesario utilizar la solución alternativa:

gcc -g -Wall -o rmk1 main.o -L. -lrmk -L/Users/jleffler/lib/64 -ljl
if [ -f rmk ] ; then mv rmk rmk2 ; else true; fi ; mv rmk1 rmk

Entonces, para evitar problemas con el 'archivo de texto ocupado', la compilación creó un nuevo archivo rmk1, luego movió el antiguo rmka rmk2(cambiar el nombre no fue un problema; desvincularlo fue), y luego movió el nuevo compilado rmk1armk .

No he visto el error en un sistema moderno en bastante tiempo ... pero no siempre tengo programas que se están reconstruyendo.

Jonathan Leffler
fuente
3
He aquí un reproductor súper rápida: echo -e '#include <unistd.h>\nint main(void){sleep (5);return 0;}' > slowprog.c && cc slowprog.c && cp a.out b.out && (./a.out &) ; sleep 1 && cp b.out a.out. Produje el mensaje de error "cp: no se puede crear el archivo normal 'a.out': archivo de texto ocupado" en mi nueva Fedora.
ArjunShankar
3
Por supuesto, esta respuesta es correcta y obtiene un +1. Es posible que desee eliminar el descargo de responsabilidad "Ha pasado un tiempo".
ArjunShankar
@ArjunShankar aquí hay una reproducción en C en un Linux moderno con llamadas de sistema "directas": stackoverflow.com/questions/16764946/… GCC solo puede sobrescribir ejecutables en ejecución hoy en día porque si primero lo hace unlinkpor defecto.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
14

Esto ocurre cuando intenta escribir en un archivo que el núcleo está ejecutando actualmente, o ejecuta un archivo que está abierto para escritura.

Fuente: http://wiki.wlug.org.nz/ETXTBSY

Messa
fuente
6

Ejemplo de reproducción mínima C POSIX ejecutable

Recomiendo comprender la API subyacente para ver mejor lo que está sucediendo.

dormir.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    sleep(10000);
}

ocupado.c

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
    int ret = open("sleep.out", O_WRONLY|O_TRUNC);
    assert(errno == ETXTBSY);
    perror("");
    assert(ret == -1);
}

Compilar y ejecutar:

gcc -std=c99 -o sleep.out ./sleep.c
gcc -std=c99 -o busy.out ./busy.c
./sleep.out &
./busy.out 

busy.outpasa las afirmaciones y perrorsalidas:

Text file busy

entonces deducimos que el mensaje está codificado en glibc.

Alternativamente:

echo asdf > sleep.out

hace la salida de Bash:

-bash: sleep.out: Text file busy

Para una aplicación más compleja, también puede observarla con strace:

strace ./busy.out

que contiene:

openat(AT_FDCWD, "sleep.out", O_WRONLY) = -1 ETXTBSY (Text file busy)

Probado en Ubuntu 18.04, kernel de Linux 4.15.0.

El error no ocurre si unlinkprimero

notbusy.c:

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
    assert(unlink("sleep.out") == 0);
    assert(open("sleep.out", O_WRONLY|O_CREAT) != -1);
}

Luego compila y ejecuta de forma análoga a la anterior, y esas afirmaciones pasan.

Esto explica por qué funciona para ciertos programas pero no para otros. Por ejemplo, si lo haces:

gcc -std=c99 -o sleep.out ./sleep.c
./sleep.out &
gcc -std=c99 -o sleep.out ./sleep.c

eso no genera un error, aunque la segunda gccllamada esté escribiendo en sleep.out.

Un rápido strace muestra que GCC primero se desvincula antes de escribir:

 strace -f gcc -std=c99 -o sleep.out ./sleep.c |& grep sleep.out

contiene:

[pid  3992] unlink("sleep.out")         = 0
[pid  3992] openat(AT_FDCWD, "sleep.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3

La razón por la que no falla es que cuando unlink y reescribe el archivo, crea un nuevo inodo y mantiene un inodo colgante temporal para el archivo ejecutable en ejecución.

Pero si solo writesinunlink , entonces intenta escribir en el mismo inodo protegido que el ejecutable en ejecución.

POSIX 7 open()

http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html

[ETXTBSY]

El archivo es un archivo de procedimiento puro (texto compartido) que se está ejecutando y oflag es O_WRONLY u O_RDWR.

hombre 2 abierto

ETXTBSY

ruta se refiere a una imagen ejecutable que se está ejecutando actualmente y se solicitó acceso de escritura.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
La razón para el caso de desvinculación es que el archivo ya no es accesible desde ese directorio, pero el inodo todavía está allí con un recuento> 0. Si reutiliza el nombre, entonces es un nuevo archivo en un nuevo inodo, mientras que si no se desvincula primero, en realidad está tratando de escribir en el inodo protegido.
Penz
@Penz obrigado por el comentario, Leandro. También me pregunto por qué, por el contrario, da un error si intentas escribir sin él unlink. ¿Linux lee alguna vez el archivo más de una vez después de la primera execllamada?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Lo que genera el ETXTBSY es la protección de inodo. Sin desvincular, cualquier escritura va al inodo que está protegido por la ejecución del archivo; con unlink, obtienes un nuevo inodo que no está protegido. (No estoy seguro de que "protegido" sea el término aquí, pero esa es la idea)
Penz
5

En mi caso, estaba tratando de ejecutar un archivo shell (con una extensión .sh) en un entorno csh, y recibí ese mensaje de error.

solo correr con bash funcionó para mí. Por ejemplo

bash file.sh

Rafayel Paremuzyan
fuente
1
¿Tenía un #!/bin/bashencabezado?
Penz
Tiene el siguiente encabezado #! / Bin / sh
Rafayel Paremuzyan
Es posible que desee intentar usar #!/usr/bin/csho equivalente.
Penz
3

Si intenta construir phpredisen un cuadro de Linux, es posible que deba darle tiempo para completar la modificación de los permisos del archivo, con un sleepcomando, antes de ejecutar el archivo:

chmod a+x /usr/bin/php/scripts/phpize \
  && sleep 1 \
  && /usr/bin/php/scripts/phpize
Stephane
fuente
No creo chmodque regrese antes de establecer los permisos. Eso podría ser un problema del sistema de archivos.
Penz
Esto ocurrió dentro de una imagen Docker que se está construyendo.
Stephane
1
Docker tiene múltiples controladores de almacenamiento, supongo que no todos son perfectos.
Penz
Aún así, es una muy buena pista para las personas que experimentan este problema al construir la imagen de Docker.
Maciej Gol
2

No sé la causa, pero puedo contribuir con una solución rápida y fácil.

Acabo de experimentar esta rareza en CentOS 6 después de "cat> shScript.sh" (pegar, ^ Z) y luego editar el archivo en KWrite. Curiosamente no hubo una instancia discernible (ps -ef) de la ejecución del script.

Mi trabajo rápido fue simplemente "cp shScript.sh shScript2.sh" y luego pude ejecutar shScript2.sh. Luego borré ambos. ¡Hecho!

ScottWelker
fuente
Su problema fue porque suspendió el catproceso. La próxima vez use ^ D, no ^ Z.
Vladimir Panteleev
Muy bien Vladimir. ¡Gracias! Eso es lo que habría hecho en el indicador de DOS / CMD. Habbits viejos ... no ha sucedido desde :)
ScottWelker
2

Puede encontrar que esto es más común en los recursos compartidos de red CIFS / SMB. Windows no permite que se escriba un archivo cuando algo más tiene ese archivo abierto, e incluso si el servicio no es Windows (podría ser algún otro producto NAS), es probable que reproduzca el mismo comportamiento. Potencialmente, también podría ser una manifestación de algún problema NAS subyacente vagamente relacionado con el bloqueo / replicación.

Cameron Kerr
fuente
2

Si está ejecutando .sh desde una conexión ssh con una herramienta como MobaXTerm, y si dicha herramienta tiene una utilidad de autoguardado para editar archivos remotos desde la máquina local, eso bloqueará el archivo.

Cerrar y volver a abrir la sesión SSH lo resuelve.

Poutrathor
fuente
1

Una de mi experiencia:

Siempre cambio el método abreviado de teclado predeterminado de Chrome mediante ingeniería inversa. Después de la modificación, olvidé cerrar Chrome y ejecuté lo siguiente:

sudo cp chrome /opt/google/chrome/chrome
cp: cannot create regular file '/opt/google/chrome/chrome': Text file busy

Usando strace, puedes encontrar más detalles:

sudo strace cp ./chrome /opt/google/chrome/chrome 2>&1 |grep 'Text file busy'
open("/opt/google/chrome/chrome", O_WRONLY|O_TRUNC) = -1 ETXTBSY (Text file busy)
Firo
fuente
0

Encontré esto en PHP cuando lo usaba fopen()en un archivo y luego lo intentaba unlink()antes de usarlo fclose().

No es bueno:

$handle = fopen('file.txt');
// do something
unlink('file.txt');

Bueno:

$handle = fopen('file.txt');
// do something
fclose($handle);
unlink('file.txt');
dtbarne
fuente
¿En las ventanas, supongo? En linux, el sistema generalmente nos permite eliminar archivos abiertos: la referencia en el directorio se elimina, pero los datos (inodo) se eliminan solo cuando el número de referencias llega a 0.
Penz
No, esto fue en Centos.
dtbarne
Lo probé en Linux 4.7.10 con el sistema de archivos ext4 y no ha producido ningún error, funcionó como mencionó Penz. Archivo eliminado con éxito. Quizás dtbarne está usando algún sistema de archivos especial.
k3a
Estaba ejecutando esto en vagabundo, podría deberse a que es una carpeta compartida.
dtbarne
0
root@h1:bin[0]# mount h2:/ /x             
root@h1:bin[0]# cp /usr/bin/cat /x/usr/local/bin/
root@h1:bin[0]# umount /x
...
root@h2:~[0]# /usr/local/bin/cat 
-bash: /usr/local/bin/cat: Text file busy
root@h2:~[126]#

ubuntu 20.04, 5.4.0-40-generic
nfsd problem, after reboot ok
Rustem
fuente
No publique solo el código como respuesta, sino que también proporcione una explicación de lo que hace su código y cómo resuelve el problema de la pregunta. Las respuestas con una explicación generalmente son más útiles y de mejor calidad, y es más probable que atraigan votos positivos.
Mark Rotteveel