Что генерирует сообщение «текстовый файл занят» в Unix?


137

Какая операция вызывает ошибку «текстовый файл занят»? Я не могу сказать точно.

Я думаю, это связано с тем, что я создаю временный скрипт python (используя tempfile) и использую из него execl, но я думаю, что execl изменяет запускаемый файл.

Ответы:


130

Эта ошибка означает, что к вашему файлу обращается какой-то другой процесс или пользователь. Используйте, lsofчтобы проверить, какие другие процессы его используют. При необходимости вы можете использовать killкоманду, чтобы убить его.


115
В частности, Text file busyошибка связана с попыткой изменить исполняемый файл во время его выполнения. «Текст» здесь относится к тому факту, что изменяемый файл является текстовым сегментом для запущенной программы. Это очень частный случай, а не тот общий, который, кажется, предполагает ваш ответ. Тем не менее, ваш ответ не совсем неверен.
ArjunShankar

4
Ответ с комментарием кажется полным.
Пенз,

OP спросил, какая операция вызывает ошибку, а не для объяснения того, что означает ошибка.
WonderWorker

Я думаю, что тот факт, что unix предполагает, что файлы являются «текстовыми файлами», нелогичен, в моем случае это был двоичный файл, который вызвал эту ошибку.
Фелипе Вальдес

1
@FelipeValdes Это историческое название по терминологии полувековой давности. Например, в мультиках текстовый сегмент программы отличался от сегмента ссылки, и даже раньше люди говорили о двоичном тексте. stackoverflow.com/a/1282540/833300
jma

30

Я давно не видел этого сообщения, но раньше оно было преобладающим в System V R3 или около того, пару десятилетий назад. Тогда это означало, что вы не могли изменить исполняемый файл программы во время ее работы.

Например, я создавал makeподобную работу под названием rmk, и через некоторое время она стала самоподдерживающейся. Я бы запустил версию для разработки и заставил ее построить новую версию. Чтобы заставить его работать, нужно было использовать обходной путь:

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

Итак, чтобы избежать проблем с «занятым текстовым файлом», сборка создала новый файл rmk1, затем переместила старый rmkв rmk2(переименование не было проблемой; разорвать связь было), а затем переместила вновь созданный файл rmk1в rmk.

Я давно не видел ошибки в современной системе ... но у меня не так уж часто программы перестраиваются сами.


3
Вот супер быстрый репродуктор: 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. Выдало сообщение об ошибке «cp: невозможно создать обычный файл 'a.out': текстовый файл занят» в моей новой Fedora.
ArjunShankar

3
Конечно, этот ответ правильный и получает +1. Возможно, вы захотите удалить заявление об отказе от ответственности "Это было давно".
ArjunShankar

@ArjunShankar вот репродукция C для современного Linux с «прямыми» системными вызовами: stackoverflow.com/questions/16764946/… GCC в настоящее время может перезаписывать только запущенные исполняемые файлы, потому что если сначала это делается unlinkпо умолчанию.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

14

Это происходит, когда вы пытаетесь выполнить запись в файл, который в данный момент выполняется ядром, или выполняете файл, который в данный момент открыт для записи.

Источник: http://wiki.wlug.org.nz/ETXTBSY


6

Минимальный пример воспроизведения POSIX на C

Я рекомендую разобраться в базовом API, чтобы лучше понять, что происходит.

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

Скомпилируйте и запустите:

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

busy.outпередает утверждения и perrorвыводит:

Text file busy

Таким образом, мы делаем вывод, что сообщение жестко закодировано в самой glibc.

В качестве альтернативы:

echo asdf > sleep.out

выводит Bash:

-bash: sleep.out: Text file busy

Для более сложных приложений вы также можете наблюдать это с помощью strace:

strace ./busy.out

который содержит:

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

Проверено на Ubuntu 18.04, ядро ​​Linux 4.15.0.

Ошибка не возникает, если вы unlinkсначала

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

Затем скомпилируйте и запустите аналогично предыдущему, и эти утверждения пройдут.

Это объясняет, почему он работает с некоторыми программами, но не работает с другими. Например, если вы это сделаете:

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

это не вызывает ошибки, даже если второй gccвызов выполняет запись sleep.out.

Быстро straceпоказывает, что GCC сначала отключается перед записью:

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

содержит:

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

Причина, по которой он не терпит неудачу, заключается в том, что когда вы unlinkи перезаписываете файл, он создает новый индексный дескриптор и сохраняет временный висячий индексный дескриптор для запущенного исполняемого файла.

Но если у вас просто writeнет unlink, тогда он пытается записать в тот же защищенный индекс, что и запущенный исполняемый файл.

POSIX 7 open()

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

[ETXTBSY]

Файл представляет собой файл чистой процедуры (общий текст), который выполняется и имеет значение O_WRONLY или O_RDWR.

человек 2 открытый

ETXTBSY

pathname относится к исполняемому образу, который в настоящее время выполняется, и был запрошен доступ на запись.


1
Обоснование случая отсоединения заключается в том, что файл больше не доступен из этого каталога, но индексный дескриптор все еще существует с refcount> 0. Если вы повторно используете имя, это будет новый файл в новом индексном дескрипторе, тогда как если вы не отсоединяете сначала, вы фактически пытаетесь записать в защищенный индексный дескриптор.
Penz

@Penz obrigado за комментарий, Леандро. Мне также интересно, почему, наоборот, выдает ошибку, если вы пытаетесь писать без него unlink. Считывает ли Linux когда-либо файл более одного раза после первого execвызова?
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Что генерирует ETXTBSY, так это защита inode. Без отсоединения все записи идут в индексный дескриптор, защищенный выполнением файла; с unlink вы получаете новый inode, который не защищен. (не уверен, что «защищенный» - это термин здесь, но идея именно в этом)
Penz

5

В моем случае я пытался запустить файл оболочки (с расширением .sh) в среде csh и получал это сообщение об ошибке.

просто работая с bash, это сработало для меня. Например

Bash file.sh


1
Был ли у него #!/bin/bashзаголовок?
Penz

Он имеет следующий заголовок #! /
Bin

Вы можете попробовать использовать #!/usr/bin/cshили аналог.
Penz

3

Если вы пытаетесь создать систему phpredisна Linux, вам может потребоваться время, чтобы завершить изменение прав доступа к файлу с помощью sleepкоманды, прежде чем запускать файл:

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

Я не думаю, chmodчто вернусь до того, как будут установлены разрешения. Это может быть проблема с файловой системой.
Penz

Это произошло внутри создаваемого образа Docker.
Стефан

1
Docker имеет несколько драйверов хранения, я думаю, не все из них идеальны.
Penz

Тем не менее, это очень хороший совет для людей, которые сталкиваются с этой проблемой при создании образа докера.
Мацей Гол

2

Не знаю причины, но я могу помочь быстро и легко решить эту проблему.

Я только что испытал эту странность в CentOS 6 после "cat> shScript.sh" (вставить, ^ Z), а затем отредактировав файл в KWrite. Как ни странно, не было заметного экземпляра (ps -ef) выполнения скрипта.

Моей быстрой работой было просто "cp shScript.sh shScript2.sh", после чего я смог запустить shScript2.sh. Потом удалил оба. Готово!


Ваша проблема была , потому что вы подвешенная на catпроцесс. В следующий раз используйте ^ D, а не ^ Z.
Владимир Пантелеев

Совершенно верно Владимир. Спасибо! Вот что я сделал бы в командной строке DOS / CMD. Старых привычек ... с тех пор не было :)
ScottWelker

2

Вы можете обнаружить, что это чаще встречается в общих сетевых папках CIFS / SMB. Windows не позволяет записывать файл, когда этот файл открыт чем-то другим, и даже если это служба, отличная от Windows (это может быть другой продукт NAS), она, скорее всего, будет воспроизводить то же поведение. Потенциально это также может быть проявлением некоторой основной проблемы NAS, нечетко связанной с блокировкой / репликацией.


2

Если вы запускаете .sh из ssh-соединения с помощью такого инструмента, как MobaXTerm, и если в указанном инструменте есть утилита автосохранения для редактирования удаленного файла с локального компьютера, это заблокирует файл.

Ее решает закрытие и повторное открытие сеанса SSH.


1

Один из моих опытов:

Я всегда меняю сочетание клавиш по умолчанию в Chrome с помощью обратного проектирования. После модификации я забыл закрыть Chrome и выполнил следующее:

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

Используя strace, вы можете найти более подробную информацию:

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

Я столкнулся с этим в PHP при использовании fopen()в файле, а затем пытался сделать unlink()это перед использованием fclose()в нем.

Не хорошо:

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

Хорошо:

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

Думаю, в окнах? В Linux система обычно позволяет нам удалять открытые файлы - ссылка в каталоге удаляется, но данные (индексный дескриптор) удаляются только тогда, когда количество ссылок достигает 0.
Penz

Нет, это было на Centos.
dtbarne

Протестировал его в Linux 4.7.10 с файловой системой ext4, и он не вызвал никаких ошибок, работал, как упоминал Пенз. Файл успешно удален. Возможно, dtbarne использует какую-то специальную файловую систему.
k3a

Запускал это на бродяге - возможно, это была общая папка.
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

Пожалуйста, не публикуйте только код в качестве ответа, но также объясните, что делает ваш код и как он решает проблему вопроса. Ответы с объяснением обычно более полезны и качественнее, и с большей вероятностью получат положительные отзывы.
Марк Роттевил,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.