트랜잭션 파일을 복사하는 방법? 있습니다. 사본은 전부 또는 아무것도

다른 파일 시스템에있는 A에서 B로 파일을 복사하고 싶습니다.

몇 가지 추가 요구 사항이 있습니다.

  1. 사본은 전부 또는 아무것도 아니며, 부분적으로 또는 손상된 파일 B가 충돌시 그대로 남아 있지 않습니다.
  2. 기존 파일 B를 덮어 쓰지 마십시오.
  3. 동일한 명령의 동시 실행과 경쟁하지 마십시오. 최대 하나만 성공할 수 있습니다.

나는 이것이 가까워 진다고 생각한다.

cp A B.part && \
ln B B.part && \
rm B.part

그러나 B.part가 존재하더라도 (-n 플래그로도) cp가 실패하지 않으면 3.이 위반됩니다. 결과적으로 다른 프로세스가 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). 그러한 파일 시스템을 감지하는 방법이 있습니까?

다른 관련이 있지만 완전히 같은 질문은 아닙니다.

파일 시스템을 통한 대략적인 원자 이동?

mv는 내 fs에 원자입니까?

eMMC에서 파일 및 디렉토리를 tempfs에서 ext4 파티션으로 원자 적으로 이동시키는 방법이 있습니까?

https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html



답변

rsync이 일을한다. 임시 파일은 O_EXCL기본적으로 생성되고 (를 사용하는 경우에만 비활성화 됨 --inplace) renamed대상 파일 위에 생성됩니다. --ignore-existing존재하는 경우 B를 덮어 쓰지 않는 데 사용하십시오 .

실제로, 나는 ext4, zfs 또는 심지어 NFS 마운트에서도 이것에 대해 어떤 문제도 경험하지 않았습니다.


답변

걱정하지 마십시오 . noclobber표준 기능 입니다.


답변

NFS에 대해 물었습니다. 검사 noclobber에는 두 개의 개별 NFS 작업 (파일이 존재하는지 확인하고 새 파일 작성)이 포함되고 두 개의 개별 NFS 클라이언트의 두 프로세스가 경쟁 조건에 빠질 수 있기 때문에 이러한 종류의 코드는 NFS에서 작동하지 않을 수 있습니다 ( 둘 다 B.part아직 존재하지 않는지 확인한 다음 성공적으로 작성하여 서로 겹쳐 씁니다.)

작성하려는 파일 시스템이 noclobber원자 와 같은 것을 지원하는지 여부에 대한 일반적인 검사는 실제로 수행하지 않습니다. 파일 시스템 유형을 NFS인지 여부를 확인할 수 있지만 휴리스틱 일 수 있으며 반드시 보장 할 필요는 없습니다. SMB / CIFS (Samba)와 같은 파일 시스템은 동일한 문제를 겪을 수 있습니다. 파일 시스템은 FUSE를 통해 노출되거나 올바르게 작동하지 않을 수 있지만 대부분 구현에 따라 다릅니다.


더 나은 방법은 B.part다른 에이전트와의 협력을 통해 고유 한 파일 이름을 사용하여 단계 에서 충돌을 피하여 에 의존 할 필요가 없도록하는 것입니다 noclobber. 예를 들어 파일 이름의 일부로 호스트 이름, PID 및 타임 스탬프 (+ 난수)를 포함 할 수 있습니다. 주어진 시간에 호스트의 특정 PID에서 단일 프로세스를 실행해야하므로, 독창성을 보장합니다.

따라서 다음 중 하나

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.

또는:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
    echo "Success creating B"
else
    echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"

따라서 두 에이전트간에 경쟁 조건이있는 경우 둘 다 작업을 진행하지만 마지막 작업은 원자 적이므로 B는 A의 전체 사본으로 존재하거나 B는 존재하지 않습니다.

복사 후 mv또는 ln작업 전에 다시 확인하여 레이스의 크기를 줄일 수 있지만 여전히 작은 경합 상태가 있습니다. 그러나 경쟁 조건에 관계없이 두 프로세스가 모두 A (또는 유효한 파일에서 원본으로 사본)를 작성하려고하면 B의 내용은 일관성이 있어야합니다.

의 첫 번째 상황 mv에서 레이스가 존재할 때 rename (2) 이 기존 파일을 원자 적으로 대체 하므로 마지막 프로세스가이기는 프로세스입니다 .

경우 newpath를가 이미 존재 액세스를 시도하는 다른 프로세스에있는 아무 소용이 없도록, 그것은 원자, 대체됩니다 newpath를가 누락 찾을 수가. […]

경우 newpath를가 존재하지만 작업이 어떤 이유로 실패, rename()보장의 인스턴스 떠날 하는 newpath을 장소에서.

따라서 당시 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), 대상 파일을 열고 모든 바이트를 읽어 대상 파일이 완전한 하나의 경우 메타 데이터는 두 가지 가능성이있다,없는 경우, 확인하기

  1. 불완전한 쓰기

  2. 다른 프로세스가 동일한 프로그램을 실행 중입니다.

대상 파일에서 열린 파일 설명 잠금을 확보하십시오.

실패는 동시 프로세스가 있음을 의미하며 현재 프로세스가 존재해야합니다.

성공은 마지막 쓰기가 중단되었음을 의미합니다. 파일을 작성하여 다시 시작하거나 수정해야합니다.

또한 fsync()파일을 닫고 잠금을 해제하기 전에 대상 파일에 기록한 후 더 나은 방법 을 사용하십시오. 그렇지 않으면 다른 프로세스가 디스크에서 아직 읽지 않은 데이터를 읽을 수 있습니다.

https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html

이는 동시에 실행중인 프로그램과 마지막으로 충돌 한 작업을 구별하는 데 도움이됩니다.


답변

cp와 함께 수행하면 올바른 결과를 얻을 수 있습니다 mv. 이렇게하면 “B”를 새로운 “A”복사본으로 바꾸거나 “B”를 그대로 둡니다.

cp A B.tmp && mv B.tmp B

기존에 맞게 업데이트 B:

cp A B.tmp && if [ ! -e B ]; then mv B.tmp B; else rm B.tmp; fi

이것은 100 % 원자가 아니지만 가깝습니다. 이 두 가지가 실행되는 경쟁 조건이 있습니다. 둘 다 동시에 if테스트에 들어가고, B존재하지 않는 것을 확인한 다음 둘 다 실행합니다 mv.


답변