Знайдіть і видаліть великі файли, відкриті, але видалені


120

Як можна знайти великі файли, які були видалені, але все ще відкриті в програмі? Як можна видалити такий файл, навіть якщо процес відкритий?

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

Просто виконайте rm output.logвилучення лише посилань на файл, але він продовжує займати місце на диску до завершення процесу. Гірше: після rming я тепер не можу знайти, де файл чи наскільки він великий! Чи є якийсь спосіб знайти файл і, можливо, випорожнити його, хоча він все ще відкритий в іншому процесі?

Я конкретно маю на увазі операційні системи на базі Linux, такі як Debian або RHEL.


2
Якщо ви знаєте pid, то ви можете використовувати lsof -p <pid>для переліку його відкритих файлів та їх розмірів. У видаленому файлі буде (deleted)поруч із ним. Видалений файл буде пов’язаний, /proc/<pid>/fd/1мабуть, Я не знаю, як зробити так, щоб процес припинив писати у його дескриптор файлів, не припиняючи його. Я думаю, це залежатиме від процесу.
доношено успішно

Дякую. Як можна отримати PID усіх rmредакторів, які ще відкриті?
dotancohen

@donothingsuccessfully "Видалений" тег, повідомлений lsof, є специфічним для Solaris, насправді Solaris 10 або пізнішої версії. ОП не вказав, яку операційну систему він використовує. @dotancohen На Solaris ви можете передавати вихід lsof для пошуку видалених, наприклад lsof | grep "(deleted)". Коли більше не буде процесів, що містять видалений файл відкритим, ядро ​​звільнить блоки inode та диска. У процесах немає "обробників", за допомогою яких вони можуть отримувати повідомлення про те, що відкритий, по суті заблокований файл, видалено з диска.
Йохан

2
@Johan, lsof | grep '(deleted)'працює і в Linux. В Linux, ви можете отримувати сповіщення про видалення файлів (навіть файли, які вже не мають жодної записи, крім / proc / some-pid / fd), за допомогою механізму inotify (IN_DELETE_SELF)
Stéphane Chazelas,

Я створив somefileі відкрив його в VIM, потім rmвідредагував його в іншому баш-процесі. Потім я запускаю, lsof | grep somefileі його немає там, хоча файл відкритий у VIM.
dotancohen

Відповіді:


141

Якщо ви не можете вбити свою програму, ви можете скоротити її замість того, щоб видалити файл журналу, щоб повернути пробіл. Якщо файл не був відкритий у режимі додавання (з O_APPEND), то файл буде здаватися таким же великим, як до наступного разу, коли програма записує його (хоча з провідною частиною розрідженою і виглядає так, ніби містить байти NUL), але пробіл буде відновлено (це не стосується файлових систем HFS + в Apple OS / X, які не підтримують розріджені файли).

Щоб усікати його:

: > /path/to/the/file.log

Якщо воно вже було видалено, в Linux, ви все одно можете його обрізати, виконавши:

: > "/proc/$pid/fd/$fd"

Де $pidідентифікатор процесу, який відкрив файл, і $fdодин дескриптор файлу, під яким він відкрився (під яким ви можете перевірити lsof -p "$pid".

Якщо ви не знаєте pid і шукаєте видалені файли, ви можете:

lsof -nP | grep '(deleted)'

lsof -nP +L1, як зазначає @ user75021 , це ще кращий (більш надійний і портативний) варіант (список файлів, що мають менше 1 посилання).

Або (в Linux):

find /proc/*/fd -ls | grep  '(deleted)'

Або знайти великих за допомогою zsh:

ls -ld /proc/*/fd/*(-.LM+1l0)

Альтернативно, якщо додаток динамічно пов’язане - приєднати до нього відладчик і зробити його викликом з close(fd)наступним новим open("the-file", ....).


1
Існує також truncateкоманда, яка робить те ж саме більш чітко.
Тобу

1
@dotancohen Stephane відредаговано, щоб включити інформацію про те, як це зробити, коли під невідомий.
Діді Кохен

1
@OlivierDulac, lsofймовірно, буде найближчим до портативного рішення, яке ви можете отримати для переліку відкритих файлів. підхід налагодження для закриття FD під ногами додатків також повинен бути досить портативним.
Стефан Шазелас

2
@StephaneChazelas: спасибі Я знайшов спосіб перерахувати всі PID-адреси, у яких відкритий файл на кожному розділі: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (а потім легко надсилати сигнали правопорушникам, щоб змусити їх звільнити fd)
Олів'є Дулак,

6
Ви також можете використовувати lsof +L1. На сторінці lsof man: "Специфікація форми +L1вибере відкриті файли, які були від'єднані. Специфікація форми +aL1 <file_system>вибирає незаповнені відкриті файли у зазначеній файловій системі." Це має бути трохи надійніше, ніж прихватування.
Синхро

31

Ознайомтеся з швидким стартовим тут: lsofQuickstart

Я здивований, що ніхто не згадав про файл швидкого запуску lsof (в комплекті з lsof). У розділі "3.a" показано, як знайти відкриті, незв'язані файли:

lsof -a +L1 *mountpoint*

Наприклад:

[root@enterprise ~]# lsof -a +L1 /tmp
COMMAND   PID   USER   FD   TYPE DEVICE    SIZE NLINK  NODE NAME
httpd    2357 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
mysqld   2588  mysql    4u   REG 253,17      52     0  1495 /tmp/ibY0cXCd (deleted)
mysqld   2588  mysql    5u   REG 253,17    1048     0  1496 /tmp/ibOrELhG (deleted)
mysqld   2588  mysql    6u   REG 253,17       0     0  1497 /tmp/ibmDFAW8 (deleted)
mysqld   2588  mysql    7u   REG 253,17       0     0 11387 /tmp/ib2CSACB (deleted)
mysqld   2588  mysql   11u   REG 253,17       0     0 11388 /tmp/ibQpoZ94 (deleted)
httpd    3457   root   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8437 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8438 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8439 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8440 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8441 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8442 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8443 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8444 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   16990 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   19595 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   27495 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   28142 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   31478 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)

У системах Red Hat для пошуку локальної копії файлу швидкого запуску я зазвичай роблю це:

[root@enterprise ~]# locate -i quickstart |grep lsof
/usr/share/doc/lsof-4.78/00QUICKSTART

... або це:

[root@enterprise ~]# rpm -qd lsof
/usr/share/doc/lsof-4.78/00.README.FIRST
/usr/share/doc/lsof-4.78/00CREDITS
/usr/share/doc/lsof-4.78/00DCACHE
/usr/share/doc/lsof-4.78/00DIALECTS
/usr/share/doc/lsof-4.78/00DIST
/usr/share/doc/lsof-4.78/00FAQ
/usr/share/doc/lsof-4.78/00LSOF-L
/usr/share/doc/lsof-4.78/00MANIFEST
/usr/share/doc/lsof-4.78/00PORTING
/usr/share/doc/lsof-4.78/00QUICKSTART
/usr/share/doc/lsof-4.78/00README
/usr/share/doc/lsof-4.78/00TEST
/usr/share/doc/lsof-4.78/00XCONFIG
/usr/share/man/man8/lsof.8.gz

1

Саме драйвер файлової системи фактично звільнить виділений простір, і це зазвичай трапляється лише після того, як будуть випущені всі дескриптори файлів, що посилаються на цей файл. Таким чином, ви не можете реально повернути місце, якщо ви не змусите програму закрити файл. Що означає або припинити його, або грати з ним «трохи» у відладчику (наприклад, закрити файл і переконатися, що він не відкритий / записаний знову, або відкриття /dev/nullзамість цього). Або ви можете зламати ядро, але я б радив цього.

Обрізання файлу, як пропонує Стефан, може допомогти, але реальний результат також залежатиме від вашої файлової системи (наприклад, попередньо виділені блоки, ймовірно, будуть звільнені лише після закриття файлу в будь-якому випадку).

Обґрунтування такої поведінки полягає в тому, що ядро ​​не знає, що робити з запитами даних (і читання, і запис, але читання насправді є більш критичним), націлене на такий файл.


2
Оскільки Linux підтримує рідкісні файли в більшості файлових систем, поведінка чітко визначена, і драйвер диска може дійсно звільнити місце на диску. Я перевірив це на ext3 та ext4, і він працює так, як писав Стефан.
jofel

1
Що змушує вас сказати, що обрізка файлу не відшкодує попередньо виділені блоки? Маніпуляція з транзакціями призначена для розміщення даних, я не маю на увазі, що в цьому немає ніякої неоднозначності.
Стефан Шазелас

1
Файлова система може зберігати виділені блоки, щоб заощадити час пізніше (особливо, якщо файл все ще залишається відкритим), особливо коли він був достатньо великий перед обрізанням. Принаймні, так виглядає те, що робить XFS.
петерф

Дякую, Петре. Я радий, що ви звертаєтесь до "чому" у цій публікації.
dotancohen

2
Наскільки я можу сказати, обрізка відкритих файлів також повертає простір на XFS. Тестується як із звичайним, так і з файлом, виділеним fallocateна Linux 4.9. Чи можете ви уточнити, під якою файловою системою та за умови обрізання файлу не набирається простір?
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.