O que gera a mensagem "arquivo de texto ocupado" no Unix?

137

Que operação gera o erro "arquivo de texto ocupado"? Eu sou incapaz de dizer exatamente.

Eu acho que está relacionado ao fato de eu estar criando um script python temporário (usando tempfile) e usando execl, mas acho que o execl altera o arquivo que está sendo executado.

Penz
fonte

Respostas:

130

Este erro significa que algum outro processo ou usuário está acessando seu arquivo. Use lsofpara verificar quais outros processos estão usando. Você pode usar o killcomando para matá-lo, se necessário.

jaypal singh
fonte
115
O Text file busyerro específico é tentar modificar um executável enquanto está em execução. O "Texto" aqui refere-se ao fato de que o arquivo que está sendo modificado é o segmento de texto para um programa em execução. Este é um caso muito especial, e não o genérico que sua resposta parece sugerir. Mesmo assim, sua resposta não está totalmente incorreta.
ArjunShankar
4
A resposta com o comentário parece completa.
Penz
O OP perguntou qual operação gera o erro, não uma explicação sobre o que o erro significa.
WonderWorker
Eu acho que o fato de o unix assumir que os arquivos são "arquivos de texto" é ilógico; no meu caso, foi um arquivo binário que causou esse erro.
Felipe Valdes
1
@FelipeValdes O nome é histórico da terminologia de meio século atrás. Por exemplo, em multics, o segmento de texto de um programa era distinto do segmento de link, e mesmo antes as pessoas falavam sobre texto binário. stackoverflow.com/a/1282540/833300
jma 11/11
30

Faz um tempo desde que eu vi essa mensagem, mas costumava prevalecer no System V R3 ou cerca de algumas décadas atrás. Naquela época, isso significava que não era possível alterar um programa executável enquanto ele estava em execução.

Por exemplo, eu estava construindo um makeworkalike chamado rmk, e depois de um tempo ele se manteve. Eu rodaria a versão de desenvolvimento e a compilaria uma nova versão. Para fazê-lo funcionar, era necessário usar a solução 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

Portanto, para evitar problemas com o 'arquivo de texto ocupado', a compilação criou um novo arquivo rmk1, depois moveu o antigo rmkpara rmk2(renomear não era um problema; desvincular era) e depois mudou o recém-criado rmk1para rmk.

Não vejo o erro em um sistema moderno há um bom tempo ... mas nem sempre os programas são reconstruídos.

Jonathan Leffler
fonte
3
Aqui está um reprodutor rápida super: 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. Produzi a mensagem de erro "cp: não é possível criar o arquivo regular 'a.out': arquivo de texto ocupado" no meu novo Fedora.
precisa saber é o seguinte
3
Obviamente, esta resposta está correta e recebe um +1. Convém remover o aviso de isenção de responsabilidade "já faz algum tempo".
ArjunShankar
@ArjunShankar aqui está uma reprodução em C em um Linux moderno com chamadas de sistema "diretas": stackoverflow.com/questions/16764946/… O GCC só pode sobrescrever os executáveis ​​em execução hoje em dia porque, se o primeiro é feito unlinkpor padrão.
Ciro Santilli escreveu
14

Isso ocorre quando você tenta gravar em um arquivo que está sendo executado atualmente pelo kernel ou executa um arquivo que está aberto para gravação no momento.

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

Messa
fonte
6

Exemplo mínimo de reprodução executável em C POSIX

Eu recomendo entender a API subjacente para ver melhor o que está acontecendo.

sleep.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

busy.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);
}

Compile e execute:

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

busy.outpassa as afirmações e perrorproduz:

Text file busy

portanto, deduzimos que a mensagem está codificada na própria glibc.

Alternativamente:

echo asdf > sleep.out

produz saída Bash:

-bash: sleep.out: Text file busy

Para uma aplicação mais complexa, você também pode observá-la com strace:

strace ./busy.out

que contém:

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

Testado no Ubuntu 18.04, Linux kernel 4.15.0.

O erro não ocorre se você unlinkprimeiro

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);
}

Em seguida, compile e execute analogamente ao acima, e essas afirmações passam.

Isso explica por que funciona em determinados programas, mas não em outros. Por exemplo, se você fizer:

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

isso não gera um erro, mesmo que a segunda gccchamada esteja sendo gravada sleep.out.

Uma rápida stracemostra que o GCC primeiro se desvincula antes de escrever:

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

contém:

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

O motivo para não falhar é que, quando você unlinkreescreve o arquivo, ele cria um novo inode e mantém um inode temporário pendente para o arquivo executável em execução.

Mas, se você apenas ficar writesem unlink, ele tenta gravar no mesmo inode protegido que o executável em execução.

POSIX 7 open()

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

[ETXTBSY]

O arquivo é um arquivo de procedimento puro (texto compartilhado) que está sendo executado e oflag é O_WRONLY ou O_RDWR.

homem 2 aberto

ETXTBSY

nome do caminho refere-se a uma imagem executável que está sendo executada no momento e o acesso de gravação foi solicitado.

Ciro Santilli adicionou uma nova foto
fonte
1
A lógica para o caso de desvinculação é que o arquivo não está mais acessível a partir desse diretório, mas o inode ainda está lá com uma refcount> 0. Se você reutilizar o nome, será um novo arquivo em um novo inode - enquanto que se você não desvincula primeiro, está realmente tentando gravar no inode protegido.
Penz
@Penz obrigado pelo comentário, Leandro. Também me pergunto por que, inversamente, isso gera um erro se você tentar escrever semunlink . O Linux já leu o arquivo mais de uma vez após a primeira execchamada?
Ciro Santilli escreveu
O que está gerando o ETXTBSY é a proteção do inode. Sem desvincular, todas as gravações vão para o inode protegido pela execução do arquivo; com o desvincular, você obtém um novo inode que não está protegido. (não tenho certeza "protegido" é o termo aqui, mas essa é a idéia)
Penz
5

No meu caso, eu estava tentando executar um arquivo shell (com uma extensão .sh) em um ambiente csh e estava recebendo essa mensagem de erro.

apenas rodando com bash funcionou para mim. Por exemplo

bash file.sh

Rafayel Paremuzyan
fonte
1
Tinha um #!/bin/bashcabeçalho?
Penz
Ele tem o seguinte cabeçalho #! / Bin / sh
Rafayel Paremuzyan 2/17/17
Você pode tentar usar #!/usr/bin/cshou equivalente.
Penz
3

Se estiver tentando construir phpredisem uma caixa do Linux, talvez seja necessário tempo para concluir a modificação das permissões do arquivo, com um sleepcomando, antes de executar o arquivo:

chmod a+x /usr/bin/php/scripts/phpize \
  && sleep 1 \
  && /usr/bin/php/scripts/phpize
Stephane
fonte
Eu não acho chmodque retornaria antes que as permissões fossem definidas. Isso pode ser um problema do sistema de arquivos.
Penz 2/17
Isso ocorreu dentro de uma imagem do Docker sendo construída.
Stephane
1
O Docker possui vários drivers de armazenamento, acho que nem todos são perfeitos.
Penz
Ainda assim, é uma dica muito boa para as pessoas que enfrentam esse problema ao criar uma imagem do docker.
Maciej Gol
2

Não sei a causa, mas posso contribuir com uma solução rápida e fácil.

Acabei de experimentar essa essa estranheza no CentOS 6 depois de "cat> shScript.sh" (colar, ^ Z) e depois editar o arquivo no KWrite. Estranhamente, não havia instância discernível (ps -ef) do script em execução.

Meu trabalho rápido foi simplesmente "cp shScript.sh shScript2.sh", depois pude executar o shScript2.sh. Então eu apaguei os dois. Feito!

ScottWelker
fonte
Seu problema foi porque você suspendeu o catprocesso. Da próxima vez, use ^ D, não ^ Z.
Vladimir Panteleev
Vladimir, certo. Obrigado! Isso é o que eu teria feito no prompt do DOS / CMD. Habbits velhos ... não acontecia desde :)
ScottWelker
2

Você pode achar que isso é mais comum em compartilhamentos de rede CIFS / SMB. O Windows não permite que um arquivo seja gravado quando algo mais o abrir, e mesmo que o serviço não seja o Windows (pode ser outro produto NAS), provavelmente reproduzirá o mesmo comportamento. Potencialmente, também pode ser uma manifestação de algum problema subjacente do NAS vagamente relacionado ao bloqueio / replicação.

Cameron Kerr
fonte
2

Se você estiver executando o .sh de uma conexão ssh com uma ferramenta como MobaXTerm, e se a ferramenta possuir um utilitário de gravação automática para editar arquivos remotos da máquina local, isso bloqueará o arquivo.

Fechar e reabrir a sessão SSH a resolve.

Poutrathor
fonte
1

Uma das minhas experiências:

Sempre altero o atalho de teclado padrão do Chrome por meio de engenharia reversa. Após a modificação, esqueci de fechar o Chrome e executei o seguinte:

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

Usando strace, você pode encontrar mais detalhes:

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
fonte
0

Me deparei com isso no PHP ao usar fopen()um arquivo e depois tentar unlink()antes de usarfclose() -lo.

Nada de bom:

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

Boa:

$handle = fopen('file.txt');
// do something
fclose($handle);
unlink('file.txt');
dtbarne
fonte
No windows eu acho? No linux, o sistema geralmente nos permite excluir arquivos abertos - a referência no diretório é eliminada, mas os dados (inode) são reduzidos somente quando o número de referências atinge 0.
Penz
Não, isso foi no Centos.
dtbarne
Testei no Linux 4.7.10 com sistema de arquivos ext4 e não produziu nenhum erro, funcionou como Penz mencionou. Arquivo excluído com sucesso. Talvez o dtbarne esteja usando algum sistema de arquivos especial.
K3A
Estava executando isso no vagrant - pode ser devido a ser uma pasta compartilhada.
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
fonte
Não publique apenas o código como resposta, mas também forneça uma explicação sobre o que seu código faz e como ele resolve o problema da pergunta. As respostas com uma explicação geralmente são mais úteis e de melhor qualidade e têm maior probabilidade de atrair votos positivos.
Mark Rotteveel