問題

問題

問題

假設我在 pathname 處有一些非目錄(檔案、命名管道/套接字等),/tmp/foo在 pathname 處有一些其他非目錄/tmp/bar。然後兩個(或更多)進程開始並發執行:

流程一的作用是:

unlink('/tmp/foo') /* or rename('/tmp/foo', '/tmp/removed') */
unlink('/tmp/bar') /* or rename('/tmp/bar', '/tmp/removed') */

流程二(依此類推)執行以下操作:

link('/tmp/foo', '/tmp/bar')

據我了解,進程二不可能成功(要么在仍然存在的link(2)情況下嘗試,在這種情況下也存在,所以它必須失敗,或者消失,所以必須失敗)。/tmp/foo/tmp/barEEXIST/tmp/fooENOENT

但這種直覺依賴於這樣的假設:unlink(2)和/或rename(2)系統呼叫在其取消連結效果中本質上是順序的,因此我正在尋找對我的理解的驗證:是否有任何類似*nix 的系統,其核心允許兩個unlink(2)和/或rename(2)調用成功,但同時也會導致link(2)成功(無論是由於重新排序/tmp/foo和的取消鏈接,/tmp/bar而不是從進程調用中抽象/隱藏它link(2),還是通過其他一些奇怪的競爭條件/錯誤)?

目前的理解

我已經閱讀了Linux 和一些 BSD 的unlink(2)rename(2)、 和手冊頁,以及這些函數的 POSIX 規格。link(2)但經過仔細考慮,我認為它們實際上並沒有在這個問題上包含任何令人放心的內容。至少rename(2),我們承諾目的地如果已經存在則被原子替換(撇開作業系統本身的錯誤不談), 但沒有別的。

我見過索賠多個同時執行的rename(foo, qux)will 以原子方式和可移植方式使除一個重命名之外的所有操作都失敗ENOENT- 所以這是有希望的!我只是不確定這是否可以擴展到在相同情況下也link(foo, bar)失敗。ENOENT

首選答案

我意識到這是「無法證明負面」情況之一 - 我們最多只能注意到沒有證據表明link(2)存在允許進程二成功的類似 *nix 的系統。

所以我正在尋找的是涵蓋盡可能多的 *nix 類系統的答案(至少是 Linux、OS X 和各種 BSD,但理想情況下還包括 Solaris 10 等仍在使用的專有系統) -來自對這些系統和這個小範圍問題(原子/有序文件系統操作)有足夠熟悉的人,他們有信心(盡可能多地)他們知道像前面提到的Mac 這樣的問題OS X -rename(2)如果它們存在於他們在熟悉的平台上,則不是實際上的原子錯誤。這會給我足夠的信心,讓我相信它的工作方式是我認為的,並且以足夠便攜的方式進行依賴。

最後說明

這不是一個“X/Y 問題”問題 - 沒有任何根本問題可以通過向我介紹各種鎖定/IPC 機製或其他解決這些特定係統調用如何交互的不確定性的方法來回答:我特別想要了解是否可以依靠上述系統呼叫在當今實際使用的*nix 類系統中按預期進行可移植互動。

答案1

查看標準,例如POSIX以保證可移植性。實際上,大多數符合 POSIX 標準的系統與規範有微小的偏差,但一般來說,您可以依賴規格中給出的保證。大多數現代 unice 都遵守該規範,即使它們尚未經過正式測試。它們可能需要在 POSIX 模式下運行,例如POSIXLY_CORRECT=1使用 bash 設定或確保其在Solaris 上/usr/xpg4/bin處於領先地位/bin/usr/binPATH

單一 Unix v2(POSIX 的舊擴充)有這樣的說法link:

link()函數將自動為現有文件建立一個新鏈接,並且文件的鏈接計數加一。

關於rename:

如果連結命名為新的參數存在,它被刪除並且老的重新命名為新的。在這種情況下,在整個重命名操作過程中,名為 new 的連結對其他進程保持可見,並且將引用由新的或者老的在手術開始之前。

POSIX 明確規定,如果目標存在,則其替換必須是原子的。然而,它並沒有聲明重命名本身必須是原子的,即不存在兩者同時發生的時間點。老的新的參考有問題的文件,或兩者都沒有時。實際上,這些屬性在 UNIX 系統上是正確的,至少在本機檔案系統上是如此。

此外,操作的順序是有保證的:在C中,;保證順序執行;在 sh 中,;/newline 保證順序執行(如 do&&等);其他程式語言也提供類似的保證。所以在

unlink("/tmp/foo");
unlink("/tmp/bar");

保證不存在/tmp/foo但不存在的時間點/tmp/bar(假設/tmp/bar最初存在)。因此並發進程執行link("/tmp/foo", "/tmp/bar")無法成功。

請注意,原子性並不能保證彈性。原子性是關於即時系統上可觀察的行為。在檔案系統的上下文中,彈性是指系統崩潰時發生的情況。許多檔案系統為了效能而犧牲了彈性,因此如果 的執行unlink("foo"); unlink("bar");中斷(目前目錄位於磁碟儲存上),則可能bar會被刪除並foo保留在後面。

當操作發生在不同客戶端時,某些網路檔案系統提供的保證較少。較舊的 NFS 實作因此而臭名昭著。我認為現代的實現更好,但我沒有現代 NFS 的經驗。

相關內容