このテストでは、最初に 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=""
との結果を比較します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 を作成する
sync
遅れている時間に驚かれた場合に備えて、dpkg
個々の fsync() 呼び出しをグローバル sync() 呼び出しに置き換えるパッチを適用すると、全体の時間が約 13 秒に抑えられることを確認できます。また、私のシステムでは、これに不十分な点はありませんでした。 dpkg
他の潜在的な副作用があるため、このアプローチの使用を中止しただけです。[1][2]
答え1
コミットの説明によると、rename()の遅延は以下が原因であると思われます。Btrfs: 新しい名前を記録した後、ログを同期するこれはカーネル v4.19 で追加されました。
新しいファイル名のログ記録 (ハードリンクの作成時または名前の変更時に発生) をログに永続化します。
このアプローチはよりシンプルであるだけでなく、ext4、xfs、f2fs (おそらく他のファイルシステムも) と同じ動作を実現します。
2番目の文は正しいとは思えません。
公平を期すために指摘しておくべきことは、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()。
私がこれを見つけた方法は、オフ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