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.

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.


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.


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


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.


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


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

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!


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.


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.


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)

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

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.