Я хочу скопировать файл из A в B, который может быть на разных файловых системах.
Есть несколько дополнительных требований:
- Копия - это все или ничего, частичный или поврежденный файл B не остается на месте при сбое;
- Не перезаписывать существующий файл B;
- Не соревнуйтесь с одновременным выполнением одной и той же команды, максимум можно добиться успеха.
Я думаю, что это близко
cp A B.part && \
ln B B.part && \
rm B.part
Но 3. нарушается сбоем cp, если B.part существует (даже с флагом -n). Впоследствии 1. может произойти сбой, если другой процесс «выиграет» cp и файл, связанный на место, будет неполным. B.part также может быть несвязанным файлом, но я счастлив потерпеть неудачу, не пытаясь использовать другие скрытые имена в этом случае.
Я думаю, что bash noclobber помогает, это работает полностью? Есть ли способ получить без требования версии bash?
#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part
Следите, я знаю, что некоторые файловые системы в любом случае не сработают (NFS). Есть ли способ обнаружить такие файловые системы?
Некоторые другие связанные, но не совсем те же вопросы:
Приближается атомарный ход по файловым системам?
Есть ли способ, чтобы атомарно переместить файл и каталог из tempfs в раздел ext4 на eMMC
https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html
mv
перезапишет существующий файл B.mv -n
не будет уведомлять, что это не удалось.ln(1)
(rename(2)
) потерпит неудачу, если B уже существует.Ответы:
rsync
делает эту работу Временный файлO_EXCL
создается по умолчанию (отключается только при использовании--inplace
), а затемrenamed
поверх целевого файла. Используйте,--ignore-existing
чтобы не перезаписывать B, если он существует.На практике у меня никогда не возникало проблем с этим на ext4, zfs или даже на NFS.
источник
Не волнуйтесь,
noclobber
это стандартная функция .источник
Вы спрашивали о NFS. Этот вид кода, скорее всего, сломается в NFS, поскольку проверка
noclobber
включает две отдельные операции NFS (проверка, существует ли файл, создание нового файла) и два процесса из двух отдельных клиентов NFS могут попасть в состояние состязания, когда оба они преуспеют ( оба проверяют, чтоB.part
еще не существует, затем оба продолжают успешно создавать его, в результате они перезаписывают друг друга.)На самом деле не нужно делать общую проверку того, будет ли файловая система, в которую вы пишете, поддерживать что-то наподобие
noclobber
атомарной или нет. Вы можете проверить тип файловой системы, будь то NFS, но это будет эвристическим и не обязательно гарантией. Файловые системы, такие как SMB / CIFS (Samba), могут страдать от тех же проблем. Файловые системы, предоставляемые через FUSE, могут или не могут вести себя правильно, но это в основном зависит от реализации.Возможно, лучший подход состоит в том, чтобы избежать столкновения на этом
B.part
этапе, используя уникальное имя файла (благодаря сотрудничеству с другими агентами), чтобы вам не нужно было зависетьnoclobber
. Например, вы можете включить, как часть имени файла, ваше имя хоста, PID и временную метку (+ возможно, случайное число). Поскольку должен быть один процесс, выполняющийся под определенным PID на хосте в любой момент времени, это должно гарантия уникальности.Так что либо одно из:
Или:
Таким образом, если у вас есть условие состязания между двумя агентами, они оба продолжат выполнение операции, но последняя операция будет атомарной, поэтому либо B существует с полной копией A, либо B не существует.
Вы можете уменьшить размер гонки, проверив еще раз после копирования и перед операцией
mv
илиln
, но там все еще есть небольшое состояние гонки. Но, независимо от состояния гонки, содержимое B должно быть согласованным, если предположить, что оба процесса пытаются создать его из A (или копии из действительного файла в качестве источника).Обратите внимание, что в первой ситуации
mv
, когда существует гонка, выигрывает последний процесс, поскольку rename (2) атомно заменит существующий файл:Таким образом, вполне возможно, что процессы, потребляющие B в то время, могут видеть разные его версии (разные inode) во время этого процесса. Если авторы всего лишь пытаются скопировать одно и то же содержимое, а читатели просто используют содержимое файла, это может быть хорошо, если они получат разные inode для файлов с одинаковым содержимым, они будут счастливы одинаково.
Второй подход с использованием жесткой ссылки выглядит лучше, но я вспоминаю, как проводил эксперименты с жесткими ссылками в узком цикле на NFS от многих одновременных клиентов и подсчитывал успех, и все же, похоже, были некоторые условия гонки, когда казалось, что два клиента выпустили жесткую ссылку операции в одно и то же время с одним и тем же пунктом назначения, похоже, были успешными. (Возможно, это поведение было связано с конкретной реализацией NFS-сервера, YMMV.) В любом случае, это, вероятно, тот же тип состояния гонки, когда вы можете получить два отдельных inode для одного и того же файла в случаях, когда есть большие параллелизм между авторами, чтобы вызвать эти условия гонки. Если ваши авторы последовательны (оба копируют от A до B), и ваши читатели потребляют только содержимое, этого может быть достаточно.
Наконец, вы упомянули блокировку. К сожалению, строго отсутствует блокировка, по крайней мере, в NFSv3 (не уверен насчет NFSv4, но я бы поспорил, что это тоже не хорошо.) Если вы рассматриваете блокировку, вы должны изучить различные протоколы для распределенной блокировки, возможно, вне полосы с фактические копии файлов, но это разрушительно, сложно и подвержено таким проблемам, как взаимоблокировки, поэтому я бы сказал, что лучше избегать.
Для получения дополнительной информации об атомарности в NFS вы можете прочитать в формате почтового ящика Maildir , который был создан, чтобы избежать блокировок и надежно работать даже в NFS. Он делает это, сохраняя уникальные имена файлов везде (так что вы даже не получите окончательный B в конце).
Возможно, несколько более интересный для вашего конкретного случая, формат Maildir ++ расширяет Maildir для добавления поддержки квоты почтового ящика и делает это путем атомарного обновления файла с фиксированным именем внутри почтового ящика (так, чтобы он был ближе к вашему B.) Я думаю, что Maildir ++ пытается добавить, что на самом деле небезопасно для NFS, но есть метод пересчета, который использует процедуру, подобную этой, и он действителен как атомарная замена.
Надеюсь, все эти указатели будут полезны!
источник
Вы можете написать программу для этого.
Используйте,
open(O_CREAT|O_RDWD)
чтобы открыть целевой файл, прочитать все байты и метаданные, чтобы проверить, является ли целевой файл полным, если нет, есть две возможности,Неполная запись
Другой процесс выполняет ту же программу.
Попробуйте установить блокировку открытого описания файла на целевой файл.
Отказ означает, что есть параллельный процесс, текущий процесс должен существовать.
Успех означает, что последняя запись завершилась сбоем, вы должны начать сначала или попытаться исправить это, записав в файл.
Также обратите внимание, что вам лучше
fsync()
после записи в целевой файл, прежде чем закрыть файл и снять блокировку, иначе другой процесс может прочитать данные, которых еще нет на диске.https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
Это важно, чтобы помочь вам отличить одновременно работающую программу от последней сбойной операции.
источник
Вы получите правильный результат, сделав
cp
вместе сmv
. Это либо заменит «B» новой копией «A», либо оставит «B», как это было раньше.Обновление для размещения существующих
B
:Это не на 100% атомно, но это близко. Есть условие гонки, при котором две из этих вещей выполняются, оба входят в
if
тест одновременно, оба видят, чтоB
не существует, затем оба выполняютmv
.источник
mv B.tmp B
не запустится, пока не запуститсяcp A B.tmp
и не вернет код результата успеха. как это провал? Кроме того, я согласен,cp A B.tmp
что перезаписать существующий,B.tmp
что вы хотите сделать. Эти&&
гарантии , что вторая команда будет работать , если и только если первый один завершается нормально.