UNIX에서 파일 추가 원자 적입니까?


106

일반적으로 여러 프로세스에서 UNIX의 파일에 추가 할 때 당연한 것으로 간주 할 수있는 것은 무엇입니까? 데이터가 손실 될 수 있습니까 (한 프로세스가 다른 프로세스의 변경 사항을 덮어 씀)? 데이터가 망가질 수 있습니까? (예를 들어, 각 프로세스는 로그 파일에 추가 할 때마다 한 줄씩 추가합니다. 두 줄이 엉망이 될 수 있습니까?) 위의 의미에서 추가가 원 자성이 아닌 경우 상호 배제를 보장하는 가장 좋은 방법은 무엇입니까?

답변:


65

'PIPE_BUF'크기 미만의 쓰기는 원자 적이어야합니다. 그것은 쉽게 더 클 수 있지만 최소한 512 바이트 여야합니다 (리눅스는 4096으로 설정되어있는 것 같습니다).

이것은 완전히 POSIX 호환 구성 요소를 모두 이야기하고 있다고 가정합니다. 예를 들어 NFS에서는 그렇지 않습니다.

그러나 'O_APPEND'모드에서 연 로그 파일에 쓰고 줄 (개행 포함)을 'PIPE_BUF'바이트 길이로 유지한다고 가정하면 손상 문제없이 로그 파일에 여러 작성자를 가질 수 있습니다. 인터럽트는 중간이 아닌 쓰기 전후에 도착합니다. 재부팅 fsync(2)후에도 파일 무결성을 유지하려면 모든 쓰기 후 호출해야 하지만 성능면에서는 끔찍합니다.

설명 : 댓글과 Oz Solomon의 답변을 읽어 보세요. 나는 O_APPEND그것이 그 PIPE_BUF크기의 원 자성 을 가져야한다고 확신하지 않습니다 . Linux가 구현 된 방식 write()일 수도 있고 기본 파일 시스템의 블록 크기 때문일 수도 있습니다.


11
정상적인 파일 시스템에서는 fsync(2)보장하는만큼 보장하며 sync(2)성능에 큰 해머 영향을주지 않습니다.
ephemient

4
확실합니까? 그 행동에 대한 링크를 제공 할 수 있습니까? 설명자가 파이프인지 확인했지만 어떤 파일 에서도 작동한다는 증거를 찾을 수 없었습니다 . NFS가 아닌 일반 파일 개체를 포함합니다.
Alan Franzoni 2011 년

6
... / write.html에서 정확히 어디에 있습니까? O_APPEND의 경우 PIPE_BUF에 대한 언급이 없으며 " 파일 오프셋 변경과 쓰기 작업 사이에 개입 파일 수정 작업이 발생하지 않는다"라는 약속이 있지만 이것이 쓰기 작업 자체가 다음과 같은 의미인지 확실하지 않습니다. 중단 ...
akavel

6
이 답변이 지적에 대해 문 PIPE_BUF해당 페이지는 파이프와 FIFO가 아니라 일반 파일에 적용됩니다.
Greg Inozemtsev

3
신호가 도착하면 bugzilla.kernel.org/show_bug.cgi?id=55651과 같이 더욱 악화 될 수 있습니다 . 왜 이것이 답변으로 표시됩니까? PIPE_BUF는 파일과 관련이 없습니다.
thinred

35

편집 : 2017 년 8 월 최신 Windows 결과로 업데이트되었습니다.

비동기 파일 시스템 및 파일 I / O C ++ 라이브러리를 구현하는 제안 된 Boost.AFIO 의 작성자로서 테스트 코드 및 결과에 대한 링크로 답변을 제공 할 것 입니다.

첫째, Windows에서 O_APPEND 또는 이에 상응하는 FILE_APPEND_DATA는 최대 파일 범위 (파일 "길이")의 증가가 동시 작성자에서 원자 적임을 의미합니다 . 이것은 POSIX에 의해 보장되며 Linux, FreeBSD, OS X 및 Windows는 모두 올바르게 구현합니다. Samba는 또한 올바르게 구현하지만 v5 이전의 NFS는 원자 적으로 추가 할 수있는 유선 형식 기능이 없기 때문에 그렇지 않습니다. 따라서 추가 전용으로 파일을 열면 NFS가 관련되지 않는 한 주요 OS에서 서로에 대해 동시 쓰기가 끊어지지 않습니다 .

그러나 원자 적 추가에 대한 동시 읽기 는 OS, 파일링 시스템 및 파일을 연 플래그에 따라 조각난 쓰기를 볼 있습니다. 최대 파일 범위의 증분은 원자 적이지만 읽기와 관련된 쓰기의 가시성은 또는 그렇지 않을 수 있습니다. 원자 적입니다. 다음은 플래그, OS 및 파일링 시스템에 따른 간략한 요약입니다.


O_DIRECT / FILE_FLAG_NO_BUFFERING 없음 :

NTFS가 포함 된 Microsoft Windows 10 : 원 자성 업데이트 = 10.0.10240까지 1 바이트, 10.0.14393에서 1Mb 이상, 아마도 무한대 (*).

ext4가있는 Linux 4.2.6 : 업데이트 원 자성 = 1 바이트

ZFS가있는 FreeBSD 10.2 : 업데이트 원 자성 = 최소 1Mb, 아마도 무한대 (*)

O_DIRECT / FILE_FLAG_NO_BUFFERING :

NTFS가 포함 된 Microsoft Windows 10 : 원 자성 업데이트 = 10.0.10240까지 페이지가 정렬 된 경우에만 최대 4096 바이트, 그렇지 않으면 FILE_FLAG_WRITE_THROUGH가 꺼져 있으면 512 바이트, 그렇지 않으면 64 바이트가 포함됩니다. 이 원자 성은 설계가 아닌 PCIe DMA의 기능 일 수 있습니다. 10.0.14393 이후, 최소 1Mb, 아마도 무한대 (*).

ext4가있는 Linux 4.2.6 : 업데이트 원 자성 = 최소 1Mb, 아마도 무한대 (*). ext4를 사용하는 이전 Linux는 확실히 4096 바이트를 초과하지 않았으며 XFS는 확실히 사용자 지정 잠금을 사용했지만 최근 Linux가 마침내이 문제를 해결 한 것처럼 보입니다.

ZFS가있는 FreeBSD 10.2 : 업데이트 원 자성 = 최소 1Mb, 아마도 무한대 (*)


https://github.com/ned14/afio/tree/master/programs/fs-probe 에서 원시 경험적 테스트 결과를 볼 수 있습니다 . 우리는 512 바이트 배수에서만 찢어진 오프셋을 테스트하므로 512 바이트 섹터의 부분 업데이트가 읽기-수정-쓰기주기 동안 찢어 질지 여부는 말할 수 없습니다.

따라서 OP의 질문에 답하기 위해 O_APPEND 쓰기는 서로 간섭하지 않지만 O_APPEND 쓰기와 동시 읽기는 O_DIRECT가 켜져 있지 않는 한 Linux에서 ext4를 사용하여 조각난 쓰기를 볼 수 있습니다. 그러면 O_APPEND 쓰기는 섹터 크기 배수 여야합니다.


(*) "Probably infinite"는 POSIX 사양의 다음 절에서 비롯됩니다.

다음 함수는 모두 일반 파일 또는 심볼릭 링크에서 작동 할 때 POSIX.1-2008에 지정된 효과에서 서로 원자 적이어야합니다. ... [많은 함수] ... read () ... write ( ) ... 두 스레드가 각각 이러한 함수 중 하나를 호출하는 경우 각 호출은 다른 호출의 지정된 모든 효과를 보거나 전혀 표시하지 않습니다. [출처]

쓰기는 다른 읽기 및 쓰기와 관련하여 직렬화 될 수 있습니다. 파일 데이터의 read ()가 데이터의 write () 후에 발생하는 것으로 (어떤 방법 으로든) 입증 될 수있는 경우, 다른 프로세스에서 호출이 이루어 지더라도 해당 write ()를 반영해야합니다. [출처]

그러나 반대로 :

POSIX.1-2008의이 볼륨은 여러 프로세스에서 파일에 대한 동시 쓰기 동작을 지정하지 않습니다. 응용 프로그램은 일종의 동시성 제어를 사용해야합니다. [출처]

이 답변에서 이것들의 의미에 대해 더 많이 읽을 수 있습니다.


29

최대 원자 추가 크기를 경험적으로 테스트하는 스크립트를 작성했습니다. bash로 작성된 스크립트는 모두 동일한 파일에 작업자 별 서명을 작성하는 여러 작업자 프로세스를 생성합니다. 그런 다음 파일을 읽고 겹치거나 손상된 서명을 찾습니다. 이 블로그 게시물 에서 스크립트의 소스를 볼 수 있습니다 .

실제 최대 원자 추가 크기는 OS뿐만 아니라 파일 시스템에 따라 다릅니다.

Linux + ext3에서 크기는 4096이고 Windows + NTFS에서 크기는 1024입니다. 더 많은 크기는 아래 설명을 참조하십시오.


Linux에서 어떤 파일 시스템으로 테스트 했습니까? 파일 시스템 블록 크기를 기반으로 한 것인지 궁금합니다.
freiheit

@freiheit 나는 ext3에서 테스트했을 때 믿습니다. 다른 FS에서 실행하고 다른 결과를 얻으면 의견을 게시하십시오.
Oz Solomon

3
@OzSolomon, 데비안 7.8에서 스크립트를 사용했고, ext4 파티션과 tmpfs 마운트 모두에서 최대 1008 바이트 (1024-16 바이트의 오버 헤드?)까지만 원자 쓰기를 얻을 수있었습니다. 그 이상의 것은 매번 부패를 초래했습니다.
Eric Pruitt 2015

6
귀하의 테스트 는 크기에 관계없이 echo $line >> $OUTPUT_FILE단일 호출이 발생 한다고 가정하는 것 같습니다 . write$line
Tomas

16

표준에 따르면 http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html .

경우] O_APPEND파일 상태 플래그의 플래그가 설정되어있는 오프셋 파일은 각각의 기록에 앞서 파일의 마지막에 설정되어야하고 중간 파일 수정 작업은 파일 오프셋 및 기록 동작 변화 사이에서 발생하지 아니한다.


20
"중간"-그러나 "중간" 이후에 내 이해를 위해 작성하는 동안 개입 은 어떻습니까? (즉 : <change_offset_action> ... "the_between_period"... <write_action>)-이에 대한 보장이 없다는 것을 이해해야합니까?
akavel

@akavel 동의했습니다. 쓰기 자체가 원자 적이라는 보장은 없습니다. 하지만 혼란 스럽습니다. 견적서에 제공된 보증에 따르면 동일한 파일을 추가하는 다중 스레드 앱이 다른 기록의 일부를 혼합하지 않는다는 결론을 내릴 수있는 것 같습니다. 그러나 OzSolomon이보고 한 실험에서 우리는 그 가정조차 위반되는 것을 알 수 있습니다. 왜?
최대

@max 죄송합니다. 질문을받지 못합니다. 첫째, OzSolomon의 실험은 다중 스레드 (단일 프로세스) 앱이 아니라 다중 프로세스입니다 . 둘째, "다중 스레드 응용 프로그램 [...]은 섞이지 않을 것" 이라는 결론을 어떻게 이끌어 내는지 이해 하지 못합니다. 내 의견에서 언급했듯이 Bastien의 인용문에 의해 보장되지 않는 것입니다. 질문을 명확히 할 수 있습니까?
akavel

2
흠 나는 그 코멘트를 썼을 때 내 논리를 재구성 할 수 없다. 네, 당신의 해석이 맞다면 물론 다른 기록이 섞일 수 있습니다. 하지만 지금은 Bastien의 인용문을 다시 읽고 있으므로 아무도 "작성하는 동안"을 중단 할 수 없음을 의미해야한다고 생각합니다. 마지막에 "쓰기"단계가 실행되면서 다른 사람이 오프셋을 이동할 수 있기 때문에
max
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.