fsync()가 먼저 호출될 때 rename()이 더 오래 걸리는 이유는 무엇입니까?

fsync()가 먼저 호출될 때 rename()이 더 오래 걸리는 이유는 무엇입니까?

이 테스트에서 fsync()가 먼저 호출될 때 rename()이 더 오래 걸리는 이유는 무엇입니까?

환경: btrfs, 기계식 HDD, Debian 9 컨테이너, 커널 5.0.17-200.fc29.x86_64에서 실행.

테스트 명령:dpkg -r linux-image-4.9.0-9-amd64 >/dev/null 2>&1 && sync && time perf_4.9 trace --no-inherit -s dpkg $FORCE_UNSAFE_IO -i linux-image-4.9.0-9-amd64_4.9.168-1_amd64.deb && time sync

FORCE_UNSAFE_IO=""vs 의 결과를 비교하세요 FORCE_UNSAFE_IO="--force-unsafe-io".

 dpkg (31632), 374488 events, 100.0%

   syscall            calls    total       min       avg       max      stddev
                               (msec)    (msec)    (msec)    (msec)        (%)
   --------------- -------- --------- --------- --------- ---------     ------
   fsync               3442 14849.586     0.002     4.314   149.959      4.11%
   rename              8463 14573.509     0.003     1.722   358.675      4.80%
   wait4                  7  8043.762     0.004  1149.109  8028.468     99.78%
   read               44025  2151.135     0.000     0.049     3.732      0.57%
   open               19301   213.628     0.002     0.011     0.375      0.90%
   write               7846   165.460     0.003     0.021     0.149      0.42%
   sync_file_range     6834    96.513     0.001     0.014     0.822      2.20%
...
real    0m41.703s
user    0m9.709s
sys 0m6.586s

real    0m0.162s
user    0m0.000s
sys 0m0.003s
 dpkg (1919), 334232 events, 100.0%

   syscall            calls    total       min       avg       max      stddev
                               (msec)    (msec)    (msec)    (msec)        (%)
   --------------- -------- --------- --------- --------- ---------     ------
   wait4                  7  8290.981     0.007  1184.426  8279.676     99.84%
   read               44399  2168.096     0.000     0.049     2.146      0.50%
   fsync                 25   653.530     0.006    26.141    68.754      8.65%
   rename              8463   522.282     0.003     0.062    69.620     22.53%
   open               12467   163.671     0.002     0.013     0.217      0.97%
   write               7846   160.979     0.003     0.021     0.356      0.50%
   sync_file_range     3417    89.676     0.010     0.026     0.841      2.05%
...
real    0m13.498s
user    0m9.643s
sys 0m5.517s

real    0m0.146s
user    0m0.000s
sys 0m0.004s

현재 전략 dpkg(예: Debian 9)은 여러분이 알고 있는 것보다 더 정교합니다. 하지만 이것이 실제로 이 사건에 영향을 미칠지는 확실하지 않습니다. 더 자세한 내용을 원하시면 이 질문에 대한 배경 지식이 있습니다.AIO fsync가 dpkg 성능을 향상시킬 수 있습니까?

이것이 관련이 있는지는 모르겠지만 일부 파일 시스템에서는 fsync()가 디렉토리를 효과적으로 동기화할 수도 있다는 생각이 들었습니다. 이는 fsync()가 반환되기 전에 새로 생성된 파일이 디스크에 표시되는지 확인하기 위한 것입니다. ext2에서는 이런 일이 발생하지 않지만 ext4에서는 발생한다는 것을 어딘가에서 읽었습니다. 부분적인 증거로 다음을 참조하세요.ext4: 이번에는 저널이 없는 상위 디렉토리를 동기화하기 위해 fsync를 만듭니다.

뒤따르는 타이밍에 놀라실 경우를 대비해 개별 fsync() 호출을 전역 sync() 호출로 대체하는 sync패치를 적용하면 전체 시간이 약 13초로 유지되는 것으로 확인됩니다 . dpkg그리고 나는 내 시스템에서 그 점에서 부적절한 것을 발견하지 못했습니다. dpkg다른 잠재적인 부작용 때문에 해당 접근 방식의 사용을 중단했습니다.[1][2]

답변1

커밋 설명에 따르면 rename() 지연은 다음으로 인해 발생할 것으로 예상됩니다.Btrfs: 새 이름을 기록한 후 로그 동기화. 이것은 커널 v4.19에 추가되었습니다.

하드 링크를 만들거나 이름을 바꿀 때 발생하는 새 파일 이름의 로깅이 로그를 유지하도록 만듭니다.

이 접근 방식은 더 간단할 뿐만 아니라 [...] ext4, xfs 및 f2fs(다른 파일 시스템에서도 가능)와 동일한 동작을 제공합니다.

나는 두 번째 문장이 옳다고 생각하지 않습니다!

공평하게 말하면 dpkg패키지가 올바르게 설치된 것으로 기록하기 전에 파일이 포함된 디렉터리를 fsync()하는 것을 잊어버렸다는 점을 지적해야 합니다. 그러나 이 btrfs 동작은 나머지 Linux와 완벽하게 일치하지 않습니다.

나는 XFS가 rename() 내부의 새 디렉토리 항목을 동기화한다고 믿지 않습니다(즉, 그것이 지속되기를 의도적으로 기다립니다). XFS rename() 내부의 동기화 쓰기에 대한 나의 추정은 부분적으로 다음 스레드를 기반으로 합니다.https://marc.info/?l=linux-xfs&m=139863577410237&w=2

ext4의 경우, 나는 다음과 같은 증거를 언급했습니다.fsync()새 디렉터리 항목이 반환되기 전에 동기화할 수 있습니다. 그러나 나는 ext4의 rename()이 그렇게 한다고 믿지 않습니다.

최근 토론에 연결했습니다.AIO fsync() 작업, 메타데이터 업데이트를 효율적으로 일괄 처리하는 방법을 설명합니다. 가상의 AIO rename()에 대해서는 많은 논의가 없었습니다. 왜냐하면 일반적인 가정은 rename()이 동기 작업이 아니라고 가정하기 때문입니다!

(btrfs는 전반적으로 약간 의심스럽습니다. 즉, 이 데이터 무결성 버그 수정은 지난 몇 가지 릴리스에 있었고, 이것이 무서운 소리로 들리는 유일한 수정은 아니었습니다.변경 로그이 릴리스의 경우).


나는 rename() 지연 BTRFS_NEED_LOG_SYNC이 마지막 줄의 반환 에 의해 트리거되어야 한다고 생각합니다.btrfs_log_new_name().

내가 이것을 찾은 방법은offcputime. 스택 추적을 통해 대기 시간을 집계합니다. 스택 추적은 다음과 같습니다.

io_schedule_timeout
wait_for_completion_io
write_all_supers
btrfs_sync_log
btrfs_sync_file
do_fsync
__x64_sys_fsync
do_syscall_64
entry_SYSCALL_64_after_hwframe
-                dpkg (23528)
    9735954

io_schedule_timeout
wait_for_completion_io
write_all_supers
btrfs_sync_log
btrfs_rename2
vfs_rename
do_renameat2
__x64_sys_rename
do_syscall_64
entry_SYSCALL_64_after_hwframe
-                dpkg (23528)
    9147785

io_schedule
bit_wait_io
__wait_on_bit
out_of_line_wait_on_bit
write_all_supers
btrfs_sync_log
btrfs_sync_file
do_fsync
__x64_sys_fsync
do_syscall_64
entry_SYSCALL_64_after_hwframe
-                dpkg (23528)
    4478158

io_schedule
bit_wait_io
__wait_on_bit
out_of_line_wait_on_bit
write_all_supers
btrfs_sync_log
btrfs_rename2
vfs_rename
do_renameat2
__x64_sys_rename
do_syscall_64
entry_SYSCALL_64_after_hwframe
-                dpkg (23528)
    4376109

관련 정보