Когда пригодится список открытых файлов
В Unix-подобных системах открытым файлом считается не только документ на диске. Сетевое соединение, канал между процессами, устройство и каталог тоже представлены дескрипторами. Утилита lsof (list open files) выводит их все, и поэтому ею пользуются в нескольких типичных ситуациях.
Первая: диск или флешка не отключаются, а система пишет, что устройство занято (в англоязычных сообщениях device is busy). Значит, какой-то процесс держит файл или рабочий каталог на этом носителе. Вторая: на диске пропало место, хотя файлы удалены. Третья: программа жалуется на «too many open files», и нужно понять, сколько дескрипторов она использует. Четвёртая: нужно выяснить, какой процесс пишет в журнал.
Для задачи «кто слушает порт» есть отдельная страница про lsof -i, здесь же речь идёт о файлах и каталогах. В macOS lsof входит в систему, в Linux его может не оказаться в минимальной установке, тогда пакет ставится через менеджер пакетов дистрибутива.
Основные способы запуска
Ключи удобно запомнить по сценариям. Все примеры подставляйте со своими именами и путями.
- lsof /путь/к/файлу показывает процессы, у которых этот файл открыт.
- lsof -p 1234 перечисляет всё, что открыл процесс с номером 1234.
- lsof -u имя_пользователя выводит открытые файлы одного пользователя.
- lsof -c имя_программы отбирает процессы по началу имени команды.
- lsof +D /путь/к/каталогу ищет открытые файлы внутри каталога рекурсивно. На больших деревьях это медленно, потому что lsof проверяет каждый элемент.
- lsof +d /путь/к/каталогу смотрит только непосредственное содержимое каталога без вложенных.
- lsof -t /путь выводит только номера процессов, без таблицы, что удобно для скриптов.
Условия по умолчанию объединяются через «или»: lsof -u user -c bash покажет и то и другое. Ключ -a превращает условия в «и». Так что lsof -a -u user -c bash оставит только процессы пользователя с именем bash.
Запуск от обычного пользователя, как правило, показывает подробности только по его собственным процессам, а полную картину по системе даёт запуск с правами администратора (sudo).
Как читать столбцы
Типичный вывод содержит столбцы COMMAND, PID, USER, FD, TYPE, DEVICE, SIZE/OFF, NODE и NAME. Нас чаще всего интересуют первый, второй, четвёртый и последний.
В столбце FD указан дескриптор. Значения cwd и txt означают рабочий каталог процесса и исполняемый файл программы. Числа с буквами, например 3r, 4w или 5u, означают файл, открытый на чтение, запись или чтение и запись. Значение mem относится к файлам, отображённым в память, например к библиотекам.
В столбце TYPE видно, что это: REG для обычного файла, DIR для каталога, CHR для символьного устройства, IPv4 и IPv6 для сетевых сокетов, unix для локальных сокетов. В NAME находится путь или описание соединения.
В Linux рядом с путём может стоять пометка (deleted): файл уже удалён из каталога, но процесс продолжает им пользоваться. Это не ошибка вывода, а нормальное состояние, которое объясняет следующий сценарий.
Место не освобождается после удаления файла
Классический случай: вы удалили огромный журнал, а команда df показывает, что место не вернулось. Причина в том, что файловая система освобождает данные только когда на файл не осталось ни одной ссылки, включая открытые дескрипторы. Пока процесс держит файл, блоки остаются занятыми, а имя в каталоге уже исчезло. Утилиты вроде du такой файл не видят, потому что просматривают только каталоги.
Найти такие файлы поможет ключ +L1: lsof +L1 выводит открытые файлы, у которых число ссылок меньше одной, то есть удалённые, но ещё используемые. По столбцу SIZE/OFF видно размер, по PID и COMMAND понятно, кто держит.
Дальше есть варианты. Корректно перезапустите или завершите процесс штатными средствами службы, и место освободится. Если вы остановить его не можете, у многих программ есть команда переоткрытия журнала. Принудительное обрезание файла через /proc/PID/fd/N в Linux возможно, но это приём для опытных администраторов, и применять его на чужих системах без понимания последствий не стоит.
Подробности о том, как свободное место и занятое место считаются разными инструментами, и о поиске крупных каталогов см. отдельные страницы про du и free.
Как узнать, сколько дескрипторов у процесса
Сообщение «too many open files» означает, что процесс упёрся в лимит числа открытых дескрипторов. Лимит задаётся для сеанса командой ulimit -n, а для служб ещё и настройками системы. Чтобы увидеть текущее потребление, посчитайте строки вывода: lsof -p 1234 | wc -l. Результат включает строку заголовка и записи mem, поэтому это оценка с погрешностью, а не точное число дескрипторов.
В Linux точнее и быстрее смотреть каталог /proc/1234/fd: количество элементов в нём равно числу дескрипторов. В macOS каталога /proc нет, поэтому там остаётся lsof.
Если число стабильно растёт со временем, возможно, программа не закрывает файлы или соединения. Это уже вопрос к разработчику или к настройкам службы. Для администратора первый шаг: собрать вывод lsof -p в два момента времени и сравнить, какого типа записей стало больше, файлов или сокетов.
Помните про ограничения: lsof может работать медленно на системах с очень большим числом процессов, а часть сведений скрыта от обычного пользователя. Когда нужно быстро проверить только порты, используйте ss в Linux, как описано на странице про ss -tulpn.
Короткая шпаргалка по сценариям
Чтобы не искать ключи каждый раз, соберите их по задачам.
- Не отключается флешка: lsof +D /точка/монтирования, найдите процесс и закройте программу, которая держит файл.
- Пропало место после удаления: lsof +L1, найдите удалённые, но открытые файлы.
- Что делает процесс: lsof -p PID.
- Что открыто у пользователя: lsof -u имя.
- Только номера процессов для скрипта: lsof -t /путь.
Перед завершением процессов, найденных через lsof, убедитесь, что вы понимаете, что они делают. Принудительное закрытие программы, пишущей на диск, может привести к потере несохранённых данных.