對不起用於添加一些舊的線程。而且做這麼長的職位。
我只知道一種方法來完成競爭條件免費rename()
在沒有鎖定的情況下,它應該在任何文件系統上都可以正常工作,即使在NFS間歇服務器重新啓動和客戶機時間扭曲就位。
下面的配方是無競爭條件的,因爲在任何情況下數據都不會丟失。它也不需要鎖,可以由不想合作的客戶執行,除了他們都使用相同的算法。
從某種意義上講,它不是競爭條件,如果某件事嚴重破壞,所有東西都會保持整齊乾淨的狀態。它也有很短的時間,在那裏既不是來源也不是目的地,然而來源仍然是另一個名字。而且對於攻擊者試圖挑起傷害的情況並沒有硬化(rename()
是罪魁禍首,去圖)。
S是源,d是目的地,P(x)是dirname(x)
,C(X,Y)是x/y
路徑的級聯
- 檢查所述目的地不存在。只是爲了確保我們不會徒勞地做下一步。
- 創建可能唯一的名稱T:= C(P(d),隨機)
- MKDIR(T),如果失敗循環到以前的步驟
- 開放(C(T, 「鎖」),O_EXCL ),如果失敗命令rmdir(T)忽略錯誤和循環到以前的步驟
- 重命名(S,C(T, 「TMP」))
- 鏈路(C(T, 「TMP」),d)
- 的unlink(C(T, 「TMP」))
- 的unlink(C(T, 「鎖定」))
- 命令rmdir(T)
算法safe_rename(S,D)
解釋說:
的問題是,我們要確保沒有競爭條件,既不在源也沒有對目的地。假設(幾乎)每一步之間可能發生任何事情,但所有其他進程在進行無競爭狀態自由重命名時遵循完全相同的算法。這包括臨時目錄T永遠不會被觸及,除非在確認(這是一個手動過程)之後,使用該目錄的進程已經死亡並且不能被複活(如在還原之後繼續虛擬機休眠)。
要正確做到rename()
,我們需要一些地方隱藏起來。所以我們構建一個目錄的方式可以確保沒有其他人(誰遵循相同的算法)意外地使用它。
但是mkdir()
不能保證在NFS上原子化。因此,我們需要確保我們有一定的保證,我們在目錄中獨處。這是lockfile上的O_EXCL
。嚴格來說,這不是鎖定,而是信號量。
除了這種罕見的情況,mkdir()
通常是原子。我們還可以創建一些使用密碼安全的隨機名稱作爲目錄,添加一些GUID,主機名和PID以確保其他人偶然選擇相同名稱的可能性不大。然而爲了證明算法是正確的,我們需要這個文件名爲lock
。
既然我們有一個大部分都是空的目錄,那麼我們就可以安全地在rename()
的源代碼那裏。這確保沒有其他人改變來源,直到我們將unlink()
它。 (好吧,內容可以改變,這不是問題。)
現在link()
技巧可以用來確保我們不覆蓋目的地。
然後unlink()
可以在剩餘的來源完成比賽條件免費。剩下的就是清理。
只有一個問題留給:
萬一link()
失敗,我們已經搬到源。爲了正確清理,我們需要將其移回。這可以通過調用safe_rename(C(T,"tmp"),S)
來完成。如果這也失敗了,我們所能做的就是嘗試儘可能多地清理(unlink(C(T,"lock"))
,),並讓管理員手動清理垃圾。
最後說明:
爲了幫助碎片情況進行清理,你都不可能使用一些更好的文件名比tmp
。聰明地選擇名字也可以使算法免受攻擊。
如果你正在移動文件的列車載入文件,你可以重複使用目錄。
但是,我同意,這個算法是純粹的矯枉過正和O_EXCL
之類的rename()
丟失。
也許您應該考慮使用顯式鎖定機制,而不是依賴隱式鎖定(包裝)重命名函數。如果創建'B'是您控制的程序,則可以使用進程間同步原語。 – 2010-07-11 09:37:55