Linuxのファイルシステム時間が常にシステム時間より数ミリ秒遅いのはなぜですか?

Linuxのファイルシステム時間が常にシステム時間より数ミリ秒遅いのはなぜですか?

Linuxでは、ファイルシステム時間が常にシステム時間より数ミリ秒遅れているように見えるため、非常に狭い時間範囲(ミリ秒)内で特定の時間の前後にファイルが変更されたことを確認しようとすると、不一致が発生する可能性があります。

ナノ秒の解像度をサポートするファイルシステムを備えたLinuxシステム(私は256バイトのinodeとZFSでext4を使ってみました)で次のことをしようとしました:

date +%H:%M:%S.%N; echo "hello" > test1; stat -c %y test1 | cut -d" " -f 2

2番目の出力値(ファイル修正時間)は、常に最初の出力値(システム時間)より数ミリ秒遅れます。たとえば、次のようになります。

17:26:42.400823099
17:26:42.395348462

反対方向でなければなりませんが、ファイルがtest1変更されたため後ろにdateコマンドを呼び出します。

Pythonでも同じ結果が得られます。

import os, time

def test():
    print(time.time())
    with open("test1", "w") as f:
        f.write("hello")
        print(os.stat("test1").st_mtime)

test()
1698255477.3125281
1698255477.3070245

なぜこれが起こるのですか?これを防ぎ、システム時間をファイルシステム時間と一致させる方法はありますか?これまで私が見つけた唯一の回避策は、ダミーの一時ファイルを次のように作成し、修正時間を取得してファイルシステム「時間」(実際に何を意味するのか)を取得することです。

def get_filesystem_time():
    """
    get the current filesystem time by creating a temporary file and getting
    its modification time.
    """
    with tempfile.NamedTemporaryFile() as f:
        return os.stat(f.name).st_mtime

しかし、よりクリーンな解決策があるかどうか疑問に思います。

答え1

ファイルタイムスタンプに使用される時間は経過した最後のタイマーであり、これは常にわずかに過去です。このcurrent_time機能はinode.c着信電話ktime_get_coarse_real_ts64:

/**
 * current_time - Return FS time
 * @inode: inode.
 *
 * Return the current time truncated to the time granularity supported by
 * the fs.
 *
 * Note that inode and inode->sb cannot be NULL.
 * Otherwise, the function warns and returns time without truncation.
 */
struct timespec64 current_time(struct inode *inode)
{
    struct timespec64 now;

    ktime_get_coarse_real_ts64(&now);

    if (unlikely(!inode->i_sb)) {
        WARN(1, "current_time() called with uninitialized super_block in the inode");
        return now;
    }

    return timestamp_truncate(now, inode);
}

後者は次の一部です以下の関数群を記録してください。:

これは、おおよそのバージョンよりも高速ですが、ユーザースペースに対応し、ユーザーCLOCK_MONOTONIC_COARSEスペースCLOCK_REALTIME_COARSEで使用できない同等のブートタイム/ tai / rawタイムベースによっては精度が低下します。

ここで返された時間は最後のタイマーティックに対応し、おそらく過去10ミリ秒(CONFIG_HZ = 100の場合)であり、「jiffies」変数を読み取るのと同じです。これらの[関数]は高速パスで呼び出された場合にのみ有用であり、まだ秒よりも良い精度を期待しますが、例えばinodeタイムスタンプの場合は「jiffies」を簡単に使用することはできません。ハードウェアクロックアクセスをスキップすると、信頼できるサイクルカウンタを持つほとんどの最新システムでは約100 CPUサイクルが節約されますが、外部クロックソースを持つ古いハードウェアでは最大数マイクロ秒まで節約できます。

inodeタイムスタンプの具体的な言及を参照してください。

カーネルを修正する以外に、これを完全に避ける方法はありません。増やすことで影響を減らすことができますCONFIG_HZ最近これを改善しようという提案がありました。、どちらまだ研究中

答え2

スティーブンキット回答正確なようです。

Cファイルシステムで使用されているのと同じ「ラフ」クロックを実際にインポートすることで、少なくとも私のカーネル構成ではこれをかなりうまく再現できます。プログラムいつも得る厚いファイルにアクセスする前のリアルタイムクロックは、ファイルのタイムスタンプを使用するか(まれに)システムクロックサイクル以前のタイムスタンプを使用します。

// excerpt from the program linked above, not a relicensing
// …
    clock_gettime(CLOCK_REALTIME_COARSE, &now_coarse);
    clock_gettime(CLOCK_REALTIME, &now);
    int fd = open("temp", O_WRONLY | O_CREAT);
    write(fd, data, length);
    close(fd);
    clock_gettime(CLOCK_REALTIME, &now_after);

    stat("temp", &props);

    printf("Differences relative to coarse clock before:\n"
           "Fine Realtime before:   %+8jd ns\n"
           "File Modification Time: %+8jd ns\n"
           "Realtime clock after:   %+8jd ns\n",
           ns_difference(&now_coarse, &now),
           ns_difference(&now_coarse, &props.st_mtim),
           ns_difference(&now_coarse, &now_after));

次のようなものを生産します

Differences relative to coarse clock before:
Fine Realtime before:   +1551810 ns
File Modification Time:       +0 ns
Realtime clock after:   +1626199 ns

前述のおおよその目盛期間デルタ状況はほとんど発生せず、now_coarseシステム目盛が正確にファイル取得と修正の間にあるときに発生します。

Differences relative to coarse clock before:
Fine Realtime before:   +1497562 ns
File Modification Time:  +999992 ns
Realtime clock after:   +1609943 ns

さて、統計資料この「ティック」が発生する10,000のイベントを収集するまで上記の操作を実行すると、可能な遅延範囲が非常に小さいことがわかります。

Total processed:     777618
Observed tick progressions:      10000
Percentage:                 0.013
Minimum tick delta:     999992
Maximum tick delta:     999993
Average tick delta: 999992.905900

言い換えれば、引用符間のデルタに関してタイミングは非常に近い。

関連情報