Comments 24
Он делает rm /var/log/app.log - запись из каталога пропала. Но nginx или java открыл этот файл на запись при старте и держит дескриптор.
Звучит как откровенное вредительство.
Это не вредительство, это обычное поведение файловой системы linux. Оно в корне отличается от того, что происходит в Windows. Пока что-то держит файл, файл после rm физически не удаляется и продолжает работать. При этом для тех, кто файл не держал, файл фактически считается удалённым. В Windows же при попытке удалить файл, пока его кто-то держит, вываливается ошибка.
Всех этих мучений, что описаны в статье, можно было бы избежать, если бы сервис не писал файлы логов самостоятельно, а доверил это какому-либо сервису по ведению логов. Там все особенности файловой системы linux уже учтены.
…со стороны «nginx или java» таки вредительство, потому что надо или понимать особенности, или «доверить это какому-либо сервису по ведению логов».
А то перекомпилировали, завелось — и радуются. А эта особенность даже домохозяйкам-на-убунте уже совершенно понятна и во многом кажется логичнее (особенно если они начинали с убунты и не выработали синдром утёнка к мастдаю).
Могу сказать только в защиту nginx. Он по умолчанию пишет сообщения самостоятельно. Но это легко меняется в настройках и он отлично пишет в journald. Особенно удобно, когда логи nginx можно просматривать совместно с логами php-fpm через journalctl. Сразу видно какой запрос пришёл, какую отладочную информацию выдал php-fpm и какие проблемы возникли при обработке запроса PHP. В java, наверно, тоже можно сделать что-то подобное.
На фоне питона они вообще все ангелы… вот уж кто считает, что не он для системы, а система для него. А равно и юзеры, и разрабы…
Особенно юзерам «весело». Каждый первый проект с гитхаба требует или полностью разобраться в использованном стеке просто для того, чтобы это всё завелось под конкретной версией, или взять отдельный ноут и циклически форматировать диск, ставя с нуля систему и под ней перебирая версии питона — авось заведётся. Где он так откладывает личинки, что даже sudo apt purge не помогает — хз.
Ну да, люди же писали, трудились, а ты хочешь, чтобы без труда всё у тебя заработало, ага. Научись из речного песка свой процессор делать, тогда удостоишься /s
Я бы скорее вредительством назвал поведение в windows, которое не дает удалить файл, даже от админа, если кто-то его держит. В древние времена была тулза Unlocker, которая могла сказать "УДАЛЯЙ" и удалялось. Давно не сижу на windows и было как-то не очень важно, вот сейчас решил спросить у gemini, оказывается Unlocker и аналоги еще живы (правда сначала он впаривал какие-то инструменты посмотреть кем открыто и его закрывать)
Скрытый текст
TL;DR: Вы абсолютно правы. Официальные современные утилиты слишком «вежливые» — они заставляют вас разбираться, кто держит файл, и вручную убивать процессы. Если вам нужен подход старого Unlocker (нажал правой кнопкой $\rightarrow$ «Удалить любой ценой» $\rightarrow$ файла нет), вам нужны сторонние утилиты, использующие драйверы уровня ядра для принудительного отрыва дескрипторов. Лучшие из них сейчас — IObit Unlocker, DeadLock и сам оригинальный Unlocker 1.9.2.
Ниже приведен подробный разбор инструментов, которые работают по принципу «вижу цель — не вижу препятствий», и объяснение того, как именно они ломают защиту Windows.
1. Программы для бескомпромиссного удаления (Аналоги старого Unlocker)
Эти инструменты не предлагают вам «подумать» или «завершить задачу». Они делают грязную работу сами: либо жестко убивают процесс-владелец, либо вырывают у него дескриптор файла на уровне ядра.
IObit Unlocker
Как работает: Добавляет пункт в контекстное меню. При выборе открывается окно, где есть кнопка-стрелка рядом с «Unlock». Выбираете «Unlock & Delete» (Разблокировать и удалить).
Результат: Программа автоматически сбрасывает дескрипторы и удаляет файл. Никаких вопросов.
Нюанс: Это коммерческий продукт, но базовая функция разблокировки бесплатна. Как и со всем софтом от IObit, при установке нужно внимательно снимать галочки, чтобы не поставить лишний рекламный мусор.
DeadLock
Как работает: Бесплатная утилита, созданная именно для агрессивного снятия блокировок. Также встраивается в Проводник.
Результат: Позволяет в пару кликов сменить владельца файла (Take Ownership) и принудительно удалить его (Unlock and Delete), даже если система сопротивляется.
LockHunter (Режим быстрого удаления)
Хотя я упоминал его ранее, стоит уточнить: в нём не обязательно смотреть на список процессов. Там есть большая кнопка «Delete It!».
Результат: Нажатие этой кнопки автоматически закроет хэндлы или убьет процессы, мешающие удалению, и отправит файл в корзину (или удалит навсегда, если зажать Shift).
Оригинальный Unlocker (от Cedrick Collomb)
Реальность: Тот самый легендарный Unlocker (версия 1.9.2) всё ещё работает даже на Windows 10 и 11.
Нюанс: Проект заброшен много лет назад. Его официальный сайт мертв. Скачивать приходится со сторонних зеркал (например, MajorGeeks).
Предупреждение: Последние версии оригинального инсталлятора содержали рекламное ПО (Delta Toolbar). Браузеры и Защитник Windows почти гарантированно пометят его как PUA/PUP (потенциально нежелательное ПО). Если решите ставить — ищите портативную версию (Portable) или внимательно снимайте галочки при установке.
2. Deep Dive: Как работает "Магия" агрессивного удаления и почему её нет в Windows
Ваше возмущение («раз я удаляю файл, значит работа с ним полностью неактуальна») логично с точки зрения пользователя. Но архитектура Windows (NTFS) выстроена иначе.
Чтобы программа могла просто «взять и удалить» заблокированный файл, она должна применить один из двух методов глубокого системного вмешательства:
Метод А: Принудительное закрытие дескриптора (Forced Handle Closure)
Когда программа (например, Word или фоновый системный процесс) открывает файл, ядро Windows выдает ей «дескриптор» (handle) — уникальный номер. В таблице объектов ядра ставится отметка, что файл заблокирован.
Утилиты вроде Unlocker устанавливают свой собственный драйвер режима ядра (ring-0). Этот драйвер:
Находит в оперативной памяти таблицу дескрипторов чужого процесса.
Вызывает недокументированные системные функции (например,
NtClose) от имени системы.Жестко удаляет запись о том, что файл занят.
Риски: Программа-владелец не знает, что у неё отобрали файл. В следующую миллисекунду она пытается записать данные по старому адресу (дескриптору), который теперь указывает в никуда или на другой системный объект. Это вызывает исключение (Exception). Если файл держал системный драйвер или антивирус, это мгновенно приводит к синему экрану (BSOD) IRQL_NOT_LESS_OR_EQUAL.
Метод Б: Отложенное удаление при загрузке (Boot-time Deletion)
Если дескриптор оторвать нельзя (например, файл занят самим ядром ОС), агрессивные утилиты используют API-функцию MoveFileEx с флагом MOVEFILE_DELAY_UNTIL_REBOOT.
Она записывает путь к файлу в специальный ключ реестра PendingFileRenameOperations. При следующей перезагрузке, еще до того, как загрузятся сервисы и графический интерфейс (до появления экрана блокировки), встроенный менеджер сеансов Windows (smss.exe) читает этот ключ и физически удаляет файл с диска.
Итог: Microsoft никогда не добавит кнопку «Удалить без вопросов» в саму Windows, потому что Метод А нарушает фундаментальную стабильность ОС и гарантии целостности данных, которые дает файловая система NTFS. Однако, как технически грамотный пользователь, вы можете использовать инструменты с драйверами уровня ядра (IObit Unlocker или старый Unlocker), принимая риск того, что программа, державшая файл, в этот момент крашнется.
Microsoft нынче разрабатывает PowerToys. Это эдакий швейцарский нож из ништяков, без которых мне уже и винда - не винда. Там есть аналог unlocker
А я как-то привык писать логи в journald. Он, и логи в сжатом виде хранит (с поиском по ним), и о ротации заботится, и чтобы диск не забивался логами (ограничение на максимальный размер). А ещё du себя очень странно ведёт на дисках с btrfs. У самой утилиты обслуживания btrfs есть свой btrfs filesystem du.
journald - да, хороший вариант, приложение может просто писать в поток и не знать вообще ни про какой файл = не держать fd, вся ротация и прочее будет разруливаться на уровне journald.
btrfs - да, есть такой момент у btrfs, поэтому лучше отдельной тулзой для btrfs - вы правы.
логи в сжатом виде хранит
Как заставить их сжиматься? У меня они занимают в 3 раза больше места чем текстовые (включенные по умолчанию Compress=yes и compact mode видимо бесполезны)
Скорее всего где-то вы ошиблись. Внимательно посмотрите на то, что там пишется. Может быть какой-то сервис сильно много чего-то пишет. В любом случае, если у вас journald, то там есть ограничение на максимальный объём файлов журнала. В случае переполнения, старые логи просто начнут удаляться и дополнительного места на диске уже не потребуется. Я бы просто запустил journalctl -f и посмотрел чего у меня там летит такого, что постоянно забивает логи. Возможно у какого-то сервиса не выключена отладка или что-то сбоит.
Это не имеет никакого значения, если тот же самый много пишущий сервис, пишущий в обычный текстовый файл вместо journald, станет занимать в 3 раза меньше места. Вышеупомянутый перенос логов nginx из текстовых файлов в journald для меня выглядит самоубийством (и это я ещё даже не вспоминал про то, что logrotate сожмёт текстовые логи после ротации)
Вышеупомянутый перенос логов nginx из текстовых файлов в journald для меня выглядит самоубийством
Рекомендую всё-таки ознакомиться с возможностями journalctl. Там есть и свой grep, и фильтр по датам, и поиск по метаданным, и аналог tail -f.... Лично мне больше понравилось. Плюс не нужно быть root, чтобы логи смотреть. Достаточно дать пользователю необходимые права. А также вместо путей /var/log… можно пользоваться просто тегами или именами сервисов (опять таки, надо смотреть что удобнее в каждом конкретном случае). Мне тоже, по началу, казалось сложно, но сейчас наоборот - ведение логов в текстовых файлах кажется дичью.
Возможности это конечно прикольно, но вот лично я не готов платить за них трёхкратным увеличением занятого места
аналог
tail -f...
С лагом в одну секунду, что при реалтаймовой отладке чего-нибудь раздражает
Плюс не нужно быть root, чтобы логи смотреть.
Достаточно быть в группе adm
С лагом в одну секунду, что при реалтаймовой отладке чего-нибудь раздражает
Ради интереса открыл journalctl -f на своём компьютере и попробовал обратиться к веб-серверу nginx локальному. За одно туда много пишет отладочной информации php-fpm, всё-таки у меня сервер стоит для разработки. Время от нажатия кнопки обновить в браузере и вывод логов занимает доли секунды. Точнее, чтобы узнать сколько, нужно что-то аппаратное, потому что глазом видно, что это происходит почти мгновенно.
Вероятно на продакшене может быть задержка, Но она связана, скорее всего, с повышением эффективности логирования. Если нагрузка большая и логов много, логично накапливать их в оперативной памяти, а потом сбрасывать на диск всем куском, чем дёргать диск на каждый чих.
А впрочем чего я теоретизирую, если могу провести эксперимент в виртуалке: настроил nginx access_log одновременно на файл и на syslog (чтобы ушло в journald), налил 100 тысяч запросов — /var/log/journal занял 96МБ (логи заранее почистил, чтобы в них был только nginx), текстовый access.log занял 27МБ (и может быть сжат gzip'ом до 4МБ)
du обходит дерево каталогов и суммирует размеры файлов
du суммирует размер не файлов, а занятых блоков. Поэтому если у вас много мелких файлов, то du насчитает намного больше, чем сумма их длин.
но всё равно не больше чем df, так же?
Абсолютно верное и важное уточнение, спасибо.
Когда процесс открывает файл, ядро создаёт… ссылку на inode
Открывает в смысле «создает» новый файл?
Когда процесс открывает файл ядро делает три вещи:
1. Находит inode - для существующего файла идёт по пути через directory entries. Для нового (O_CREAT) - сначала создаёт inode, потом directory entry на него.
2. Создаёт struct file - это объект в памяти ядра: указатель на inode, текущая позиция в файле (offset), флаги (O_RDONLY и т.д.). Это и есть "ссылка на inode".
3. Кладёт fd в таблицу процесса - файловый дескриптор (число 3, 4, 5...) это просто индекс в таблице который указывает на struct file.
df врёт. du врёт. Где на самом деле гигабайты?