Що генерує повідомлення «текстовий файл зайнятий» в Unix?


137

Яка операція створює помилку "текстовий файл зайнятий"? Я не можу точно сказати.

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

Відповіді:


130

Ця помилка означає, що якийсь інший процес або користувач отримує доступ до вашого файлу. Використовуйте, lsofщоб перевірити, які інші процеси використовують. Ви можете використовувати killкоманду, щоб вбити її, якщо це потрібно.


115
Text file busyПомилка в конкретному про спробу змінити виконуваний файл під час його виконання. Тут "Текст" посилається на те, що модифікований файл є текстовим сегментом для запущеної програми. Це дуже особливий випадок, а не загальний, про який, здається, пропонує ваша відповідь. Тим не менш, ваша відповідь не зовсім неправильна.
ArjunShankar

4
Відповідь із коментарем здається повною.
Пенц

ОП запитав, яка операція створює помилку, а не для пояснення того, що означає помилка.
WonderWorker

Я думаю, що факт, що unix передбачає, що файли є "текстовими файлами", є ілогічним, в моєму випадку це був двійковий файл, який спонукав до цієї помилки.
Феліпе Вальдес

1
@FelipeValdes Назва є історичною з термінології півстоліття тому. Наприклад, у мультиках текстовий сегмент програми відрізнявся від сегмента посилань, а ще раніше люди говорили про двійковий текст. stackoverflow.com/a/1282540/833300
СОУ

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(перейменування не було проблемою, Unlink було), а потім переїхав недавно побудований 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 тут є відтворенням C на сучасному Linux із "прямими" системними викликами: stackoverflow.com/questions/16764946/… GCC сьогодні може замінити лише запущені виконавчі файли, тому що якщо спочатку це unlinkза замовчуванням.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

14

Це відбувається під час спроби запису у файл, який наразі виконується ядром, або виконання файлу, відкритого для запису.

Джерело: http://wiki.wlug.org.nz/ETXTBSY


6

Мінімальний приклад відтворення C POSIX

Рекомендую розібратися в базовому API, щоб краще зрозуміти, що відбувається.

сон.c

#define _XOPEN_SOURCE 700
#include <unistd.h>

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

занято.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перезаписуванні файлу він створює новий inode і зберігає тимчасовий висячий inode для запущеного виконуваного файлу.

Але якщо ви просто writeбез цього unlink, то він намагається записати в той самий захищений inode, що і запущений виконуваний файл.

POSIX 7 open()

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

[ETXTBSY]

Файл являє собою чисту процедуру (спільний текст), який виконується, а oflag - O_WRONLY або O_RDWR.

людина 2 відкрити

ETXTBSY

ім'я шляху відноситься до виконуваного зображення, яке зараз виконується, і запит на доступ до запису.


1
Обґрунтування випадку від’єднання полягає в тому, що файл більше не доступний з цього каталогу, але inode все ще є із перерахунком> 0. Якщо ви повторно використовуєте ім'я, то це новий файл у новому inode - тоді як якщо ви не від’єднаєте спочатку, ви насправді намагаєтесь записати в захищений inode.
Пенз

@Penz obrigado для коментаря, Леандро. Мені також цікаво, чому, навпаки, це дає помилку, якщо ви намагаєтеся писати без unlink. Чи читає Linux ніколи не один раз після першого execдзвінка?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Що генерує ETXTBSY, це захист від inode. Без від’єднання будь-які записи переходять до inode, який захищений виконанням файлу; при від’єднанні ви отримуєте новий inode, який не захищений. (не впевнений, що тут "захищений", але це ідея)
Пенз,

5

У моєму випадку я намагався виконати файл оболонки (з розширенням .sh) у середовищі csh, і я отримував це повідомлення про помилку.

просто біг з башем, це працювало на мене. Наприклад

bash file.sh


1
У нього був #!/bin/bashзаголовок?
Пенз

Він має такий заголовок №! / Bin / sh
Рафайел Паремузян

Ви можете спробувати використати #!/usr/bin/cshабо еквівалент.
Пенц

3

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

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

Я не думаю, chmodщо повернеться до встановлення дозволів. Це може бути проблема файлової системи.
Пенз

Це сталося всередині побудованого зображення Докера.
Стефан

1
У Docker є кілька драйверів для зберігання даних, я думаю, не всі вони ідеальні.
Пенз

Тим не менш, це дуже хороший підказ для людей, які відчувають цю проблему під час створення іміджу докера.
Maciej Gol

2

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

Я щойно зазнав цього дивацтва на CentOS 6 після "cat> shScript.sh" (paste, ^ 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 система зазвичай дозволяє видаляти відкриті файли - посилання в каталозі видаляється, але дані (inode) видаються лише тоді, коли кількість посилань досягає 0.
Пенз

Ні, це було на 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.