Find and remove large files that are open but have been deleted
How does one find large files that have been deleted but are still open in an application? How can one remove such a file, even though a process has it open? The situation is that we are running a process that is filling up a log file at a terrific rate. I know the reason, and I can fix it. Until then, I would like to rm or empty the log file without shutting down the process. Simply doing rm output.log removes only references to the file, but it continues to occupy space on disk until the process is terminated. Worse: after rm ing I now have no way to find where the file is or how big it is! Is there any way to find the file, and possibly empty it, even though it is still open in another process? I specifically refer to Linux-based operating systems such as Debian or RHEL.
If you know the pid then you can use lsof -p
@donothingsuccessfully The «deleted» tag reported by lsof is Solaris specific, in fact Solaris 10 or later only. The OP did not specify what operating system he is using. @dotancohen On Solaris you can pipe the output of lsof to search for deleted, eg lsof | grep «(deleted)» . When there are no more processes holding a deleted file open, the kernel will free up the inode and disk blocks. Processes do not have «handlers» by which they can be notified that an open, essentially locked file, have been removed from disk.
@Johan, the lsof | grep ‘(deleted)’ works on Linux as well. On Linux, you can be notified of file deletion (even files that already don’t have any entry in any directory other than /proc/some-pid/fd anymore) with the inotify mechanism (IN_DELETE_SELF event)
I created somefile and opened it in VIM, then rm ed it in another bash process. I then run lsof | grep somefile and it is not in there, even though the file is open in VIM.
4 Answers 4
If you can’t kill your application, you can truncate instead of deleting the log file to reclaim the space. If the file was not open in append mode (with O_APPEND ), then the file will appear as big as before the next time the application writes to it (though with the leading part sparse and looking as if it contained NUL bytes), but the space will have been reclaimed (that does not apply to HFS+ file systems on Apple OS/X that don’t support sparse files though).
If it was already deleted, on Linux, you can still truncate it by doing:
Where $pid is the process id of the process that has the file opened, and $fd one file descriptor it has it opened under (which you can check with lsof -p «$pid» .
If you don’t know the pid, and are looking for deleted files, you can do:
lsof -nP +L1 , as mentioned by @user75021 is an even better (more reliable and more portable) option (list files that have fewer than 1 link).
find /proc/*/fd -ls | grep '(deleted)'
Or to find the large ones with zsh :
An alternative, if the application is dynamically linked is to attach a debugger to it and make it call close(fd) followed by a new open(«the-file», . ) .
@OlivierDulac, lsof is probably going to be the closest to a portable solution you can get to list open files. the debugger approach to close the fd under the application feet should be quite portable as well.
@StephaneChazelas: thanks. I found a way to list all PIDs which have a file open on each partitions : df -k | awk ‘NR>1 < print $NF >‘ | xargs fuser -Vud (and then easy to send signals to the offenders to force them to release the fd)
You can also use lsof +L1 . From the lsof man page: «A specification of the form +L1 will select open files that have been unlinked. A specification of the form +aL1
Check out the quickstart here: lsof Quickstart
I’m surprised no one mentioned the lsof quickstart file (included with lsof). Section «3.a» shows how to find open, unlinked files:
[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)
On Red Hat systems to find the local copy of the quick-start file, I usually do this:
[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
Удаление открытого файла в Linux
Есть ряд каверзных вопросов по Linux, которые вводят в ступор большинство начинающих системных администраторов Linux. В топе уже наверно полтора десятилетия держится вопрос про удаление открытого файла в Linux.
Есть ряд каверзных вопросов по Linux, которые вводят в ступор большинство начинающих системных администраторов Linux. В топе уже наверно полтора десятилетия держится вопрос про удаление открытого файла в Linux.
Такие вопросы очень любят задавать на собеседованиях бывалые админы, а в интернете про ответы на них не написал только ленивый. Тем не менее кандидаты все также из раза в раз продолжают делать круглые глаза. Максимум, что от них можно услышать — это «Иноды. Я слышал про иноды, но больше про них ничего не знаю».
В этой статье раз и навсегда внесём ясность в этом вопросе.
Удаление открытого файла в Linux
Чтобы проверить как работает файловая система в Linux, проведем небольшой эксперимент.
Подготовка
Для проведения всех тестов нам необходима виртуальная машина с практически любым дистрибутивом Linux и дополнительный диск. Благо в век виртуализации это сделать проще простого. Для наглядности возьмем диск в пару гигабайт и заранее подготовим его (базовые операции с дисками рассмотрим в другой статье).
Представим, что вы впервые вошли по ssh на сервер, у вас рутовые права и ваша задача просто разобраться с дисковой подсистемой.
Свободное место и свободные иноды
Первое, что мы сделаем, это проверим диски всем знакомой командой df (лишние данные убраны из вывода):
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/test_disk_01-lv_test_01 1.5G 4.5M 1.4G 1% /mnt/data
Утилита отображает занятое на файловой системе место в блоках 1К. Этой информации в большинстве случаев хватает лишь для констатации факта о % свободного места и не стоит на этом останавливать диагностику.
Проблемы с записью новых файлов может вызвать также нехватка инодов (inodes), поэтому полезно будет их проверить той же командой, но с другим ключом:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/test_disk_01-lv_test_01 98304 10 98294 1% /mnt/data
Используется 1% места на разделе. Запоминаем вывод команд, он нам понадобится для последующего анализа.
Создание файлов на диске
Далее создадим один большой файл на нашем диске. Сделать это проще всего утилитой dd, которая поставляется по умолчанию вместе с системой (предварительно создадим пару каталогов):
dd if=/dev/zero of=big/file.img bs=1G count=1
И ещё создадим 10 маленьких файлов, но другим способом:
Теперь снова смотрим два вывода df:
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/test_disk_01-lv_test_01 1.5G 1.1G 359M 75% /mnt/data
Как видно, теперь на диске занято 75%. А что с инодами?
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/test_disk_01-lv_test_01 98304 23 98281 1% /mnt/data
Количество инод изменилось на 13, хотя файлов мы создали всего 11.
Эксперимент с удалением открытого файла
Теперь посмотрим на ситуацию другой утилитой, которую также обязательно нужно использовать для диагностики. Речь о du:
Примечание: для изучения ключей наберите в консоли man du. Актуально также и для других утилит, рассматриваемых в статье.
Теперь сымитируем что-то похожее на блокировку файла (если будут предупреждения, соглашайтесь):
# less +F big/file.img >/dev/null &
Команда просто открывает файл на чтение и отправляет задание в бэкграунд. Теперь удаляем файл:
После этого проверяем свободно место:
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/test_disk_01-lv_test_01 1.5G 1.1G 359M 75% /mnt/data
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/test_disk_01-lv_test_01 98304 23 98281 1% /mnt/data
В итоге место не освободилось, хотя файл, казалось бы, удален. Инодов используется столько же. Но может быть du покажет нам что-то другое:
Занято 8Кб. Ок, есть ещё одна утилита, которая лучше других объяснит что происходит, это lsof:
COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
less 16483 root 3r REG 253,2 1073741824 11 /mnt/data/big/file.img (deleted)
Мы посмотрели все, что хотели, можно заканчивать.
Завершение эксперимента
Сворачиваем наш эксперимент — убиваем процесс, который мы ранее запустили в фоновом режиме:
Теперь снова проверяем свободное место:
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/test_disk_01-lv_test_01 1.5G 4.5M 1.4G 1% /mnt/data
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/test_disk_01-lv_test_01 98304 22 98282 1% /mnt/data
Большой файл удалился, место и иноды освободились. Смысла проверять вывод du нет, она покажет то же самое, что и в предыдущий раз.
Выводы
Что все же произошло? А произошло следующее: информация от du и df до удаления большого файла была очевидной и объяснений не требует. За исключением одного момента — почему файлов создали 11, а количество инодов увеличилось на 13? Тут все просто. Наверно все слышали выражение, что все в линуксе — файл 1? И каталог тоже, а их мы создали два.
Далее мы удалили файл, который открыт на чтение другим процессом. Команда rm удалила ссылку на файл, которую хранит объект каталога, но не смогла удалить файл физически с диска, поскольку файл был открыт на чтение другой программой.
Примечание: догадываетесь почему нельзя чистить логи путем простого удаления файлов без перезапуска приложения?
Хоть файл уже и не имел имени, но все ещё имел файловый дескриптор (= инод), к которому продолжала обращаться программа. Это было также хорошо заметно по выводу lsof — файл был помечен как удаленный. Как только программу остановили, файл освободился и система смогла завершить начатое — удалить файл и зависимые структуры данных на диске окончательно.
В показаниях утилиты du также нет ничего странного, ведь она считывает все перечисленные имена файлов в каталоге и оценивает их размер. Поскольку ссылку на имя большого файла удалили, du не смогла оценить его объем, зато это смогла сделать df, ведь она оценивает реальный занятый объем на диске в блоках без привязки к именам. Вот весь секрет.