僅適用於同一目錄中的檔案。

僅適用於同一目錄中的檔案。

在 Linux 上,openat系統呼叫可用於建立檔案並測試它們是否存在。就 C/C++ 記憶體模型而言,建立檔案並驗證其存在會建立同步關係。我需要知道的是這些同步是否都是順序一致的。 (我當然希望如此,但我實際上還沒有在任何地方看到過這個記錄。)

例如,給定進程 p1 和 p2,以及路徑 A 和 B:

如果 p1 這樣做:建立(A),然後建立(B)

p2 執行以下操作:嘗試開啟(B),然後嘗試開啟(A)

且沒有其他進程幹擾A或B,是否有可能p2成功開啟B但找不到A?

如果有什麼不同,我們可以假設所有操作都在一個檔案系統中。

答案1

透過所有底層磁碟和多核心 CPU 最佳化,不一定能夠確定兩個進程之間操作序列的嚴格順序。這就是如果存在時間相關行為的可能性則使用信號量的原因。

答案2

僅適用於同一目錄中的檔案。

有6條規則:

  1. 讀取存取權限。鎖定規則:呼叫者鎖定我們正在存取的目錄。鎖被共享。

  2. 物件創建。鎖定規則:與上面相同,但鎖定是排他的。

  3. 物體移除。鎖定規則:呼叫者鎖定父級,找到受害者,鎖定受害者並呼叫方法。鎖是獨佔的。

  4. rename()那是不是跨目錄。鎖定規則:呼叫者鎖定父級並尋找來源和目標。在交換的情況下(使用 RENAME_EXCHANGE標誌參數)鎖定兩者。無論如何,如果目標已經存在,請鎖定它。如果來源是非目錄,則鎖定它。如果我們需要鎖定兩者,請按 inode 指標順序鎖定它們。然後調用該方法。所有鎖都是獨佔的。注意:我們可能會鎖定共享的來源(以及交換情況下的目標)。

  5. 連結創建。鎖定規則:

    • 鎖定父級

    • 檢查來源不是目錄

    • 鎖源

    • 呼叫該方法。所有鎖都是獨佔的。

  6. 跨目錄重命名。整個群中最棘手的。鎖定規則:

    • 鎖定檔案系統

    • 將父母鎖定在「祖先第一」的順序。

    • 找到來源和目標。

    • 如果舊父級等於或是目標的後代,則失敗 -ENOTEMPTY

    • 如果新父級等於或是來源的後代,則失敗 -ELOOP

    • 如果是交換,請鎖定來源和目標。

    • 如果目標存在,則鎖定它。如果來源是非目錄,則鎖定它。如果我們需要鎖定兩者,請按照 inode 指標順序進行操作。

    • 呼叫該方法。全部->i_rwsem採取獨家。同樣,我們可能會鎖定共享的來源(以及交換情況下的目標)。

上述規則顯然保證了所有將要透過方法讀取、修改或刪除的目錄都將被呼叫者鎖定。

鎖定強制線性化,因此操作在單一目錄上完全有序。但是,讀取存取 (1)、物件建立 (2) 和物件刪除 (3) 不會採用比目錄鎖更廣泛的鎖,因此無法保證不同目錄中目錄操作的順序;不同的觀察者可能會看到目錄的線性歷史以不同的方式交織。

相關內容