Вопрос или проблема
У меня есть объемный диск, смонтированный на /ram
, который обычно хранит временные данные, которые, как я знаю, не нужно сохранять. Файловая система объемного диска – f2fs
, созданная с помощью zram-generator
с этой конфигурацией:
# /etc/systemd/zram-generator.conf
[zram0]
zram-size = ram
fs-type = f2fs
compression-algorithm = zstd
mount-point = /ram
options = nosuid,nodev,noatime,X-mount.mode=1777,X-mount.group=ramuser,atgc,gc_merge,lazytime
Он будет смонтирован с этими параметрами:
$ findmnt /ram
TARGET SOURCE FSTYPE OPTIONS
/ram /dev/zram0 f2fs rw,nosuid,nodev,noatime,lazytime,background_gc=on,gc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,alloc_mode=default,checkpoint_merge,fsync_mode=posix,atgc,memory=normal,errors=continue
Это работает для всех моих целей, как я и планировал. (Обычно у меня много текстовых файлов, которые сильно выигрывают от сжатия.)
Теперь у меня есть несколько виде файлов, которые я хочу конвертировать в другой формат. Они мне не нужны на диске, и я не против, если они будут утеряны после отключения питания, так что у меня они только в памяти.
Для конвертации у меня есть такой цикл:
for i in *.mkv; do
ffmpeg -i "${i}" "${i:r}.mp4" # конвертировать mkv в mp4
rm -v "${i}" # удалить оригинал
done
(Команда ffmpeg сокращена, она работает.)
Есть 6 файлов mkv по 2 ГБ каждый, итого занимает 12 ГБ. Это нормально. Конвертированные mp4 примерно того же размера, так что во время выполнения цикла должно использоваться максимум 12+2 ГБ. Параметр -v
для rm
показывает удаление файла после конвертации.
Однако память заполняется, и система в какой-то момент зависает. Когда я прерываю цикл (нажимая Ctrl+c), память наконец освобождается. Таким образом, старые файлы mkv не очищаются, пока цикл не завершится.
Держит ли этот for i in *.mkv
все файлы, так что их нельзя удалить, и они очищаются только после завершения цикла? lsof
не показывает, что старые файлы все еще используются.
Файл удален (ls
не показывает его), но кажется, что он не очищается.
Ответ или решение
Ваша проблема связана с тем, как оболочка Bash обрабатывает массив файлов в цикле. Когда вы используете конструкцию for i in *.mkv
, Bash фактически создает массив имен файлов, соответствующих этому шаблону, в момент запуска цикла. Это означает, что даже если вы удаляете файлы внутри цикла с помощью команды rm
, память может не быть немедленно освобождена до завершения цикла.
Вот несколько рекомендаций, которые могут помочь вам решить эту проблему:
-
Использование команды
find
: Вместо использования циклаfor
, рассмотреть возможность использования командыfind
. Она может обрабатывать файлы по одному и будет освобождать память после удаления каждого файла.Пример команды:
find . -name "*.mkv" -exec sh -c ' for file; do ffmpeg -i "$file" "${file%.mkv}.mp4" # конвертация mkv в mp4 rm -v "$file" # удаление оригинала done ' sh {} +
-
Увеличение размера OOM (Out Of Memory): Если вы можете позволить себе больше памяти, вы можете настроить параметры вашей среды, чтобы предоставить больше ресурсов, учитывая, что с
zram
вы работаете в ограниченном пространстве. -
Добавление
sync
: После командыrm
вы можете попробовать добавлятьsync
, чтобы немедленно очистить кэш файловой системы и освободить используемую память:rm -v "${i}" sync
-
Оптимизация
ffmpeg
: Убедитесь, что настройка конвертации файлов оптимизирована, чтобы она использовала минимальный объем памяти. Проверьте параметрыffmpeg
, которые могут влиять на использование памяти во время конвертации. -
Использование
tmpfs
: Если проблема с памятью вызывает постоянные проблемы, рассмотрите возможность создания временной файловой системыtmpfs
вместо использованияzram
. Это обеспечит более предсказуемое поведение, посколькуtmpfs
управляет памятью и файлом более эффективно.
Следуя этим рекомендациям, вы сможете предотвратить переполнение памяти и обеспечить более эффективное удаление файлов в процессе конвертации.