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

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

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

환경: btrfs, 기계식 하드 디스크, 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=""FORCE_UNSAFE_IO="--force-unsafe-io"vs. 결과를 비교해 보세요 .

 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가 로그 없이 상위 디렉터리를 동기화하도록 합니다.

sync추적 시간에 놀랐 다면 dpkg개별 fsync() 호출을 전역 sync() 호출로 대체하는 패치를 적용하면 전체 시간이 약 13초로 단축되는 것 같습니다. 내 시스템에서 어떤 결함도 발견하지 못했습니다. 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() 작업, 그리고 메타데이터 업데이트의 효율적인 일괄 처리를 가능하게 하는 방법을 설명합니다. 일반적으로 rename()은 동기 작업이 아니라고 가정하기 때문에 가상의 AIO rename()에 대해서는 많은 논의가 없습니다.

(일반적으로 btrfs는 나에게 약간 수상쩍게 보입니다. 즉, 지난 몇 번의 릴리스에서 이 데이터 무결성 버그 수정이 이루어진 것을 보았으며, 끔찍하게 들리는 유일한 것은 아닙니다.변경 로그이 버전의 경우).


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

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

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

관련 정보