Co generuje komunikat „plik tekstowy zajęty” w systemie Unix?


145

Jaka operacja generuje błąd „plik tekstowy zajęty”? Nie jestem w stanie powiedzieć dokładnie.

Myślę, że jest to związane z faktem, że tworzę tymczasowy skrypt w Pythonie (używając pliku tymczasowego) i używam z niego execl, ale myślę, że execl zmienia uruchamiany plik.

Odpowiedzi:


138

Ten błąd oznacza, że ​​inny proces lub użytkownik uzyskuje dostęp do Twojego pliku. Użyj, lsofaby sprawdzić, jakie inne procesy go używają. W killrazie potrzeby możesz użyć polecenia, aby go zabić.


117
Text file busyBłąd w szczególnych wynosi około próbuje modyfikować plik wykonywalny, gdy jest on wykonywany. „Tekst” odnosi się tutaj do faktu, że modyfikowany plik jest segmentem tekstowym dla uruchomionego programu. Jest to bardzo szczególny przypadek, a nie ogólny, który sugeruje twoja odpowiedź. Mimo to twoja odpowiedź nie jest całkowicie błędna.
ArjunShankar

5
Odpowiedź wraz z komentarzem wydaje się kompletna.
Penz

OP zapytał, która operacja generuje błąd, a nie w celu wyjaśnienia, co oznacza błąd.
WonderWorker,

Myślę, że fakt, iż unix zakłada, że ​​pliki są „plikami tekstowymi”, jest nielogiczny, w moim przypadku był to plik binarny, który spowodował ten błąd.
Felipe Valdes

1
@FelipeValdes Nazwa jest historyczna z terminologii sprzed pół wieku. Na przykład w multics segment tekstowy programu był różny od segmentu łącza, a nawet wcześniej ludzie mówili o tekście binarnym. stackoverflow.com/a/1282540/833300
jma

30

Minęło trochę czasu, odkąd widziałem tę wiadomość, ale była powszechna w Systemie V R3 lub w tym miejscu dobre kilka dekad temu. W tamtych czasach oznaczało to, że nie można było zmienić pliku wykonywalnego programu podczas jego działania.

Na przykład makebudowałem workalike o nazwie rmk, a po chwili sam się utrzymywał. Uruchomiłbym wersję rozwojową i kazałbym zbudować nową wersję. Aby to zadziałało, konieczne było zastosowanie obejścia:

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

Tak więc, aby uniknąć problemów z `` plikiem tekstowym zajętym '', kompilacja utworzyła nowy plik rmk1, a następnie przeniosła stary rmkdo rmk2(zmiana nazwy nie była problemem; odłączenie było), a następnie przeniosła nowo zbudowany plik rmk1do rmk.

Nie widziałem błędu w nowoczesnym systemie od dłuższego czasu ... ale nie zawsze mam programy, które same się odbudowują.


3
Oto super szybkie reproducer: 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. Wyświetla komunikat o błędzie „cp: nie można utworzyć zwykłego pliku 'a.out': plik tekstowy zajęty” na mojej nowej Fedorze.
ArjunShankar

@ArjunShankar tutaj jest reprodukcją C na nowoczesnym Linuksie z "bezpośrednimi" wywołaniami systemowymi: stackoverflow.com/questions/16764946/ ... GCC może obecnie nadpisywać tylko uruchomione pliki wykonywalne, ponieważ jeśli najpierw robi to unlinkdomyślnie.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功


9

Minimalna, działająca reprodukcja C POSIX

Zalecam zrozumienie podstawowego interfejsu API, aby lepiej zobaczyć, co się dzieje.

spać. c

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

zajęty. 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);
}

Skompiluj i uruchom:

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

busy.outprzekazuje potwierdzenia i dane perrorwyjściowe:

Text file busy

więc wnioskujemy, że wiadomość jest zakodowana na stałe w samym glibc.

Alternatywnie:

echo asdf > sleep.out

sprawia, że ​​wyjście Bash:

-bash: sleep.out: Text file busy

W przypadku bardziej złożonej aplikacji można to również zaobserwować za pomocą strace:

strace ./busy.out

który zawiera:

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

Testowane na Ubuntu 18.04, jądro Linux 4.15.0.

Błąd nie występuje, jeśli unlinkpierwszy

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

Następnie skompiluj i uruchom analogicznie do powyższego, a te potwierdzenia przejdą.

To wyjaśnia, dlaczego działa w niektórych programach, ale nie w innych. Np. Jeśli:

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

to nie generuje błędu, nawet jeśli drugie gccwywołanie to zapis sleep.out.

Szybkie stracepokazuje, że GCC pierwsze odłącza przed zapisem:

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

zawiera:

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

Powodem, dla którego nie kończy się niepowodzeniem, jest to, że po unlinkponownym zapisaniu pliku tworzy on nowy i-węzeł i utrzymuje tymczasowo wiszący i-węzeł dla działającego pliku wykonywalnego.

Ale jeśli po prostu writenie unlink, to stara się pisać do tego samego węzła zabezpieczono jako uruchomiony wykonywalny.

POSIX 7 open()

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

[ETXTBSY]

Plik jest plikiem czystej procedury (współużytkowany tekst), który jest wykonywany, a oflag to O_WRONLY lub O_RDWR.

człowiek 2 otwarty

ETXTBSY

pathname odnosi się do wykonywalnego obrazu, który jest aktualnie wykonywany i zażądano dostępu do zapisu.

źródło glibc

Szybkie grep na 2.30 daje:

sysdeps/gnu/errlist.c:299:    [ERR_REMAP (ETXTBSY)] = N_("Text file busy"),
sysdeps/mach/hurd/bits/errno.h:62:  ETXTBSY                        = 0x4000001a,        /* Text file busy */

i ręczne trafienie w manual/errno.texi:

@deftypevr Macro int ETXTBSY
@standards{BSD, errno.h}
@errno{ETXTBSY, 26, Text file busy}
An attempt to execute a file that is currently open for writing, or
write to a file that is currently being executed.  Often using a
debugger to run a program is considered having it open for writing and
will cause this error.  (The name stands for ``text file busy''.)  This
is not an error on @gnuhurdsystems{}; the text is copied as necessary.
@end deftypevr

2
Uzasadnieniem dla przypadku rozłączenia jest to, że plik nie jest już dostępny z tego katalogu, ale i-węzeł nadal tam jest z liczbą referencyjną> 0. Jeśli ponownie użyjesz tej nazwy, jest to nowy plik w nowym i-węźle - podczas gdy jeśli nie odłączasz najpierw, tak naprawdę próbujesz pisać do chronionego i-węzła.
Penz

@Penz obrigado za komentarz, Leandro. Zastanawiam się też, dlaczego, przeciwnie, daje błąd, jeśli próbujesz pisać bez unlink. Czy Linux kiedykolwiek czyta plik więcej niż raz po pierwszym execwywołaniu?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

To, co generuje ETXTBSY, to ochrona i-węzłów. Bez odłączania wszelkie zapisy trafiają do i-węzła chronionego przez wykonanie pliku; po odłączeniu otrzymasz nowy i-węzeł, który nie jest chroniony. (nie jestem pewien, czy jest to termin „chroniony”, ale taki jest zamysł)
Penz

5

W moim przypadku próbowałem wykonać plik powłoki (z rozszerzeniem .sh) w środowisku csh i otrzymałem ten komunikat o błędzie.

po prostu bieganie z bashem zadziałało dla mnie. Na przykład

bash plik.sh


1
Czy miał #!/bin/bashnagłówek?
Penz

Ma następujący nagłówek #! / Bin / sh
Rafayel Paremuzyan

Możesz spróbować użyć #!/usr/bin/cshlub odpowiednika.
Penz

3

Jeśli próbujesz zbudować phpredisna komputerze z systemem Linux, być może będziesz musiał dać mu czas na dokończenie modyfikacji uprawnień do pliku za pomocą sleeppolecenia przed uruchomieniem pliku:

chmod a+x /usr/bin/php/scripts/phpize \
  && sleep 1 \
  && /usr/bin/php/scripts/phpize

Nie sądzę, chmodaby powrócił przed ustawieniem uprawnień. To może być problem z systemem plików.
Penz

Wystąpiło to wewnątrz budowanego obrazu Dockera.
Stephane

1
Docker ma wiele sterowników pamięci masowej, myślę, że nie wszystkie z nich są idealne.
Penz

Mimo to jest to bardzo dobra wskazówka dla osób, które mają ten problem podczas tworzenia obrazu dockera.
Maciej Gol

2

Może się to okazać bardziej powszechne w przypadku udziałów sieciowych CIFS / SMB. System Windows nie pozwala na zapisanie pliku, gdy coś innego ma otwarty ten plik, a nawet jeśli usługą nie jest system Windows (może to być inny produkt NAS), prawdopodobnie odtworzy to samo zachowanie. Potencjalnie może to być również przejaw jakiegoś podstawowego problemu z NAS, niejasno związanego z blokowaniem / replikacją.


2

Jeśli uruchamiasz plik .sh z połączenia ssh za pomocą narzędzia takiego jak MobaXTerm i jeśli wspomniane narzędzie ma narzędzie automatycznego zapisywania do edycji zdalnego pliku z komputera lokalnego, spowoduje to zablokowanie pliku.

Zamknięcie i ponowne otwarcie sesji SSH rozwiązuje problem.


2

Nie znam przyczyny, ale mogę pomóc w szybkim i łatwym rozwiązaniu.

Właśnie doświadczyłem tej dziwności w CentOS 6 po cat > shScript.sh(wklej, ^Z) następnie edycji pliku w KWrite. Co dziwne, nie było zauważalnej instancji ( ps -ef) wykonywanego skryptu.

Moja szybka praca polegała na cp shScript.sh shScript2.shtym, że byłem w stanie wykonać shScript2.sh. Następnie usunąłem oba. Gotowe!


1
Twój problem wynikał z zawieszeniacat procesu. Następnym razem użyj ^ D, a nie ^ Z.
Vladimir Panteleev

Całkiem dobrze Vladimir. Dzięki! To właśnie zrobiłbym w wierszu poleceń DOS / CMD. Stare nawyki ... nie zdarzyły się od tego czasu :)
ScottWelker

1

Jedno z moich doświadczeń:

Zawsze zmieniam domyślny skrót klawiaturowy Chrome za pomocą inżynierii wstecznej. Po modyfikacji zapomniałem zamknąć Chrome'a ​​i uruchomiłem:

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

Korzystając ze strace, możesz znaleźć więcej szczegółów:

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

Natknąłem się na to w PHP podczas używania fopen()na pliku, a następnie próbowałem unlink()to przed użyciem fclose()na nim.

Nie dobrze:

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

Dobry:

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

Chyba na oknach? W Linuksie system zwykle pozwala nam usuwać otwarte pliki - odwołanie w katalogu jest eliminowane, ale dane (i-węzeł) są fredowane tylko wtedy, gdy liczba odniesień osiągnie 0.
Penz

Nie, to było na Centosie.
dtbarne

Przetestowałem go na Linuksie 4.7.10 z systemem plików ext4 i nie spowodowało to żadnego błędu, działało tak, jak wspomniał Penz. Plik został pomyślnie usunięty. Może dtbarne używa specjalnego systemu plików.
k3a

Uruchomiłem to na vagrancie - może to być spowodowane tym, że jest to folder współdzielony.
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

Nie wysyłaj tylko kodu jako odpowiedzi, ale także wyjaśnij, co robi twój kod i jak rozwiązuje problem. Odpowiedzi z wyjaśnieniem są zwykle bardziej pomocne i lepszej jakości oraz częściej przyciągają głosy za.
Mark Rotteveel
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.