파일을 덮어쓸 때 파일을 닫으면 동기화를 기다리지만 생성할 때는 동기화를 기다리지 않는 이유는 무엇입니까?

파일을 덮어쓸 때 파일을 닫으면 동기화를 기다리지만 생성할 때는 동기화를 기다리지 않는 이유는 무엇입니까?

이 스크립트를 실행할 때:

#!/usr/bin/env python3
f = open("foo", "w")
f.write("1"*10000000000)
f.close()
print("closed")

내 Ubuntu 컴퓨터에서 다음 프로세스를 관찰할 수 있습니다.

메모리는 10GB로 꽉 찼습니다. 페이지 캐시는 10GB의 더티 페이지로 채워집니다. (/proc/meminfo)는 "close"를 인쇄하고 스크립트가 종료됩니다. 잠시 후 더티 페이지 수가 감소합니다.

그러나 "foo" 파일이 이미 존재하는 경우 close()는 모든 더티 페이지가 다시 기록될 때까지 차단됩니다.

이 행동의 이유는 무엇입니까?

파일이 존재하지 않는 경우 strace는 다음과 같습니다.

openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
ioctl(3, TCGETS, 0x7ffd50dc76f0)        = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
ioctl(3, TCGETS, 0x7ffd50dc76c0)        = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
lseek(3, 0, SEEK_CUR)                   = 0
mmap(NULL, 10000003072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcd9892e000
mmap(NULL, 10000003072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcb4486f000
write(3, "11111111111111111111111111111111"..., 10000000000) = 2147479552
write(3, "11111111111111111111111111111111"..., 7852520448) = 2147479552
write(3, "11111111111111111111111111111111"..., 5705040896) = 2147479552
write(3, "11111111111111111111111111111111"..., 3557561344) = 2147479552
write(3, "11111111111111111111111111111111"..., 1410081792) = 1410081792
munmap(0x7fcb4486f000, 10000003072)     = 0
munmap(0x7fcd9892e000, 10000003072)     = 0
close(3)                                = 0
write(1, "closed\n", 7closed
)                 = 7
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fcfedd5cf20}, {sa_handler=0x62ffc0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fcfedd5cf20}, 8) = 0
sigaltstack(NULL, {ss_sp=0x2941be0, ss_flags=0, ss_size=8192}) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}, NULL) = 0
exit_group(0)                           = ?
+++ exited with 0 +++

다음은 strace입니다(있는 경우).

openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
ioctl(3, TCGETS, 0x7fffa00b4fe0)        = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
ioctl(3, TCGETS, 0x7fffa00b4fb0)        = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
lseek(3, 0, SEEK_CUR)                   = 0
mmap(NULL, 10000003072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71de68b000
mmap(NULL, 10000003072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6f8a5cc000
write(3, "11111111111111111111111111111111"..., 10000000000) = 2147479552
write(3, "11111111111111111111111111111111"..., 7852520448) = 2147479552
write(3, "11111111111111111111111111111111"..., 5705040896) = 2147479552
write(3, "11111111111111111111111111111111"..., 3557561344) = 2147479552
write(3, "11111111111111111111111111111111"..., 1410081792) = 1410081792
munmap(0x7f6f8a5cc000, 10000003072)     = 0
munmap(0x7f71de68b000, 10000003072)     = 0
close(3#### strace will block exactly here until write-back is completed ####)                                = 0 
write(1, "closed\n", 7closed
)                 = 7
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f7433ab9f20}, {sa_handler=0x62ffc0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f7433ab9f20}, 8) = 0
sigaltstack(NULL, {ss_sp=0x1c68be0, ss_flags=0, ss_size=8192}) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}, NULL) = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Python file-io를 사용하는 대신 단순히 파일을 인쇄하고 파이핑할 때와 cout에 인쇄하는 작은 동등한 C++ 프로그램을 사용하여 동일한 작업을 수행할 때 동일한 동작이 관찰될 수 있습니다. 실제 시스템 호출이 차단되는 것 같습니다.

답변1

O_PONIES그것은 우리의 11번째 생일을 기념했던 최근의 실패를 상기시키는 것처럼 들립니다 .

ext4가 등장하기 전에 ext3은 정전 시에도 안정적이라는 평판을 얻었습니다. 거의 손상되지 않으며 파일의 데이터가 손실되는 경우도 거의 없습니다. 그런 다음 ext4는 데이터 블록의 지연 할당을 추가합니다. 즉, 파일 데이터를 디스크에 즉시 쓰려고 시도하지도 않습니다. 일반적으로 데이터가 어느 시점에 도착하면 문제가 되지 않지만, 임시 파일의 경우 데이터를 디스크에 전혀 쓸 필요가 없을 수도 있습니다.

하지만 ext4는 씁니다.메타데이터변경 사항 및 파일에 특정 변경 사항이 발생했음을 기록합니다. 이제 시스템이 충돌하면 파일이 잘린 것으로 표시되지만 이후의 쓰기는 디스크에 저장되지 않습니다(블록이 할당되지 않기 때문). 따라서 ext4에서는 충돌 후 최근에 수정된 파일이 0 길이로 잘리는 것을 자주 볼 수 있습니다.

물론 이것이 정확히 대부분의 사용자가 원하는 것은 아니지만, 자신의 데이터에 많은 관심을 갖고 있는 앱은 를 호출해야 한다고 주장할 수도 있습니다 fsync().이름 바꾸기, 또한 fsync()(또는 적어도 fdatasync()) 디렉토리를 포함해야 합니다. 그러나 이를 수행하는 사람은 거의 없습니다. 부분적으로 ext3에서는 fsync()전체 디스크가 동기화되어 관련 없는 데이터가 많이 포함될 수 있기 때문입니다. (또는 가능한 한 전체 디스크에 가까워도 차이는 중요하지 않습니다.)

이제 ext3은 성능이 좋지 않지만 fsync()ext4는 fsync()파일 손실이 필요하지 않습니다. 대부분의 애플리케이션이 적절한 순간에 호출을 수행하는 엄격한 댄스보다 신경을 덜 쓰는 것보다 파일 시스템별 동작을 구현하는 것을 고려하면 fsync()이는 좋은 상황이 아닙니다. 분명히 파일 시스템이 존재하는지 알아내는 것은 쉽지 않습니다.예전에는먼저 ext3 또는 ext4로 설치하십시오.

마지막으로 ext4 개발자는 가장 일반적이고 중요해 보이는 상황에 대해 몇 가지 변경 사항을 적용했습니다.

  • 다른 파일 위에 한 파일의 이름을 바꿉니다. 실행 중인 시스템에서 이는 일반적으로 파일의 새 버전을 배치하는 데 사용되는 원자성 업데이트입니다.
  • 귀하의 경우 기존 파일을 덮어씁니다. 이는 실행 중인 시스템에서는 원자적이지 않지만 일반적으로 응용 프로그램이 파일을 자르는 대신 교체하려고 함을 의미합니다. 덮어쓰기에 실패하면 손실됩니다.구 버전파일의 내용이므로 정전이 발생하면 최신 데이터만 손실되는 완전히 새로운 파일을 만드는 것과는 약간 다릅니다.

내가 아는 한, XFS는 ext4 이전에도 충돌 후 유사한 길이가 0인 파일을 표시합니다. 나는 이것을 한 번도 따라한 적이 없어서 그들이 어떤 종류의 수정을 할지 모르겠습니다.

수정 사항을 언급하는 LWN의 다음 기사를 참조하십시오.ext4 및 데이터 손실(2009년 3월)

물론 당시 이에 대한 다른 기사도 있었지만 대부분 비난하는 문제였기 때문에 해당 기사에 연결하는 것이 도움이 될지 확신할 수 없습니다.

답변2

이것은 Linux 자체에 관한 것이 아니라 ext4에 관한 것입니다. 이 효과는 btrfs에서는 발생하지 않습니다.

ext4놀랍게도 이런 현상은 mount 옵션에서도 발생합니다 data=writeback.

관련 정보