Chcę skopiować plik z A do B, który może znajdować się w różnych systemach plików.
Istnieją pewne dodatkowe wymagania:
- Kopia jest w całości lub w ogóle, brak częściowego lub uszkodzonego pliku B po awarii;
- Nie zastępuj istniejącego pliku B;
- Nie konkuruj z jednoczesnym wykonywaniem tego samego polecenia, co najwyżej można odnieść sukces.
Myślę, że to się zbliża:
cp A B.part && \
ln B B.part && \
rm B.part
Ale 3. zostaje naruszone przez cp nie zawodzi, jeśli B.part istnieje (nawet z flagą -n). Następnie 1. może się nie powieść, jeśli inny proces „wygra” cp, a plik połączony na miejscu jest niekompletny. B.part może być również niepowiązanym plikiem, ale cieszę się, że nie udało mi się spróbować innych ukrytych nazw w tym przypadku.
Myślę, że bash noclobber pomaga, czy to działa w pełni? Czy istnieje sposób na obejście się bez wymagania wersji bash?
#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part
W dalszym ciągu wiem, że niektóre systemy plików i tak się nie sprawdzą (NFS). Czy istnieje sposób na wykrycie takich systemów plików?
Niektóre inne powiązane, ale niezupełnie te same pytania:
Przybliżanie ruchu atomowego między systemami plików?
Czy mv jest atomowy na moim FS?
jest sposób na atomowe przenoszenie pliku i katalogu z tempfs na partycję ext4 na eMMC
https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html
mv
nadpisze istniejący plik B.mv -n
nie powiadomi o niepowodzeniu.ln(1)
(rename(2)
) zakończy się niepowodzeniem, jeśli B już istnieje.Odpowiedzi:
rsync
wykonuje tę pracę. Plik tymczasowy jestO_EXCL
tworzony domyślnie (wyłączony, jeśli używasz--inplace
), a następnierenamed
nad plikiem docelowym. Użyj,--ignore-existing
aby nie zastępować B, jeśli istnieje.W praktyce nigdy nie spotkałem się z tym z żadnymi problemami na wierzchowcach ext4, zfs, a nawet NFS.
źródło
Nie martw się,
noclobber
to standardowa funkcja .źródło
Zapytałeś o NFS. Ten rodzaj kodu najprawdopodobniej
noclobber
ulegnie awarii w systemie plików NFS, ponieważ sprawdzenie dotyczy dwóch osobnych operacji NFS (sprawdź, czy plik istnieje, utwórz nowy plik), a dwa procesy z dwóch oddzielnych klientów NFS mogą przejść w stan wyścigu, w którym oba się powiedzie ( oba sprawdzają, żeB.part
jeszcze nie istnieje, a następnie oba z powodzeniem go tworzą, w wyniku czego się wzajemnie nadpisują).Naprawdę nie ma potrzeby ogólnego sprawdzania, czy system plików, w którym piszesz, będzie obsługiwał coś takiego jak
noclobber
atomowy, czy nie. Możesz sprawdzić typ systemu plików, czy to NFS, ale byłby to heurystyka i niekoniecznie gwarancja. Systemy plików takie jak SMB / CIFS (Samba) prawdopodobnie cierpią z powodu tych samych problemów. Systemy plików narażone przez FUSE mogą, ale nie muszą zachowywać się poprawnie, ale to zależy głównie od implementacji.Prawdopodobnie lepszym rozwiązaniem jest uniknięcie kolizji na
B.part
etapie, poprzez użycie unikalnej nazwy pliku (dzięki współpracy z innymi agentami), dzięki czemu nie trzeba polegaćnoclobber
. Na przykład możesz dołączyć jako część nazwy pliku nazwę hosta, identyfikator PID i znacznik czasu (+ ewentualnie liczbę losową). Ponieważ w danym momencie powinien istnieć pojedynczy proces o określonym identyfikatorze PID na hoście, powinno to być gwarantuje wyjątkowość.Więc jedno z:
Lub:
Więc jeśli masz warunki wyścigu między dwoma agentami, obaj będą kontynuować operację, ale ostatnia operacja będzie atomowa, więc albo B istnieje z pełną kopią A, albo B nie istnieje.
Możesz zmniejszyć rozmiar wyścigu, sprawdzając ponownie po kopii i przed operacją
mv
lubln
, ale nadal istnieje mały warunek wyścigu. Jednak niezależnie od warunków wyścigu zawartość B powinna być spójna, zakładając, że oba procesy próbują utworzyć ją z A (lub kopii z prawidłowego pliku jako źródła).Zauważ, że w pierwszej sytuacji
mv
, gdy wyścig istnieje, ostatni wygrywa ten proces, ponieważ zmiana nazwy (2) atomowo zastąpi istniejący plik:Jest więc całkiem możliwe, że procesy zużywające B w tym czasie mogą zobaczyć różne jego wersje (różne i-węzły) podczas tego procesu. Jeśli wszyscy autorzy próbują po prostu skopiować tę samą zawartość, a czytelnicy po prostu zużywają zawartość pliku, to może być w porządku, jeśli otrzymają różne i-węzły dla plików o tej samej zawartości, będą zadowoleni tak samo.
Drugie podejście z wykorzystaniem twardego łącza wygląda lepiej, ale przypominam sobie przeprowadzanie eksperymentów z linkami twardymi w ciasnej pętli na NFS od wielu równoległych klientów i liczenie sukcesu, i nadal zdawały się istnieć pewne warunki wyścigowe, gdzie wydawało się, że dwóch klientów wydało hardlink Operacja w tym samym czasie, z tym samym miejscem docelowym, wydawała się udana. (Możliwe, że to zachowanie było związane z konkretną implementacją serwera NFS, YMMV.) W każdym razie jest to prawdopodobnie ten sam rodzaj wyścigu, w którym możesz uzyskać dwa osobne i-węzły dla tego samego pliku, w przypadkach, gdy jest ciężki współbieżność między pisarzami, aby uruchomić te warunki wyścigu. Jeśli twoi autorzy są konsekwentni (oba kopiują od A do B), a czytelnicy zużywają tylko zawartość, może to wystarczyć.
Wreszcie wspomniałeś o blokowaniu. Niestety blokowanie jest poważnie brakuje, przynajmniej w NFSv3 (nie jestem pewien co do NFSv4, ale założę się, że to też nie jest dobre). Jeśli zastanawiasz się nad blokowaniem, powinieneś rozważyć różne protokoły blokowania rozproszonego, być może poza pasmem z rzeczywiste kopie plików, ale jest to zarówno destrukcyjne, złożone, jak i podatne na problemy, takie jak zakleszczenia, więc powiedziałbym, że lepiej tego unikać.
Aby uzyskać więcej informacji na temat atomowości na NFS, możesz przeczytać w formacie skrzynki pocztowej Maildir , który został stworzony w celu uniknięcia blokad i niezawodnej pracy nawet w NFS. Robi to, utrzymując wszędzie unikalne nazwy plików (dzięki czemu nie dostajesz nawet końcowego B na końcu).
Być może nieco bardziej interesujący dla twojego konkretnego przypadku, format Maildir ++ rozszerza Maildir o obsługę limitu skrzynki pocztowej i robi to poprzez atomową aktualizację pliku o stałej nazwie w skrzynce pocztowej (aby być bliżej twojego B.) Myślę, że Maildir ++ próbuje do dołączania, co nie jest tak naprawdę bezpieczne w systemie plików NFS, ale istnieje metoda przeliczania, która wykorzystuje procedurę podobną do tej i jest ważna jako zamiana atomowa.
Mam nadzieję, że wszystkie te wskaźniki będą przydatne!
źródło
Możesz do tego napisać program.
Użyj,
open(O_CREAT|O_RDWD)
aby otworzyć plik docelowy, przeczytaj wszystkie bajty i metadane, aby sprawdzić, czy plik docelowy jest kompletny, jeśli nie, istnieją dwie możliwości,Niekompletne pisanie
Inny proces uruchamia ten sam program.
Spróbuj uzyskać otwartą blokadę opisu pliku na pliku docelowym.
Niepowodzenie oznacza, że istnieje równoległy proces, obecny proces powinien istnieć.
Sukces oznacza, że ostatni zapis się zawiesił, powinieneś zacząć od nowa lub spróbować naprawić, pisząc do pliku.
Zauważ też, że lepiej byłoby
fsync()
po zapisaniu do pliku docelowego przed zamknięciem pliku i zwolnieniem blokady, w przeciwnym razie inny proces może odczytać dane, które nie znajdują się jeszcze na dysku.https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
Jest to ważne, aby pomóc Ci rozróżnić między jednocześnie uruchomionym programem a operacją, która zakończyła się awarią.
źródło
Otrzymasz poprawny wynik, robiąc
cp
wspólnie zmv
. Spowoduje to albo zastąpienie „B” świeżą kopią „A”, albo pozostawienie „B”, jak było wcześniej.aktualizacja w celu dostosowania istniejących
B
:To nie jest w 100% atomowy, ale się zbliża. Występują warunki wyścigu, w których dwie z tych rzeczy są uruchomione, obie przystępują do
if
testu w tym samym czasie, obie widzą, żeB
nie istnieje, a następnie obie wykonująmv
.źródło
mv B.tmp B
nie uruchomi się, dopókicp A B.tmp
pierwszy nie uruchomi się i nie zwróci kodu wyniku sukcesu. jak to jest porażka? zgadzam się również, żecp A B.tmp
nadpiszeB.tmp
to, co chcesz zrobić. Na&&
gwarancji, że 2nd komenda uruchomi wtedy i tylko wtedy, gdy pierwszy zakończy się normalnie.