假設兩個不同的行程獨立打開同一個檔案,因此在Open file table (system-wide)中有不同的條目。但它們指的是相同的 i 節點條目。
由于檔案描述符參考Open file table (system-wide)中的不同條目,因此它們可能具有不同的檔案偏移量。write由于檔案偏移量不同,是否會有競爭條件的機會?內核如何避免它?
書籍:Linux 編程介面;頁碼 95; 第 5 章(檔案 I/O:更多細節);第 5.4 節
uj5u.com熱心網友回復:
(我假設是因為您使用write()該問題指的是 POSIX 系統。)
每個write()操作都應該是完全原子的,假設是 POSIX 系統(假定使用write())。
根據POSIX 7 的2.9.7 執行緒與常規檔案操作的互動:
以下所有函式在對常規檔案或符號鏈接進行操作時,在 POSIX.1-2017 中指定的效果中相互之間應是原子的:
chmod() chown() close() creat() dup2() fchmod() fchmodat() fchown() fchownat() fcntl() fstat() fstatat() ftruncate() lchown() link() linkat() lseek() lstat() open() openat() pread() read() readlink() readlinkat() readv() pwrite() rename() renameat() stat() symlink() symlinkat() truncate() unlink() unlinkat() utime() utimensat() utimes() write() writev()如果兩個執行緒各自呼叫這些函式之一,則每個呼叫要么看到另一個呼叫的所有指定效果,要么一個都看不到。無論何時成功關閉檔案描述符(例如,由于呼叫 close()、呼叫 dup2() 或行程終止),對 close() 函式的要求也應適用。
但要特別注意(加粗礦)的規范:write()
該
write()函式應嘗試寫入nbyte位元組...
POSIX 說write()對檔案的呼叫應該是原子的。POSIX 并沒有說write()呼叫會完成。 這是一個 Linux 錯誤報告,其中信號中斷了write()部分完成的信號。注意解釋:
現在,就規范(POSIX,SUS,...)而言,這是完全有效的行為(如果我遺漏了什么,請糾正我)。所以我會說這個程式是不正確的。但是OTOH我同意這在a50527b1之前是不可能的,我們不想破壞用戶空間。我不想恢復那個提交,因為它允許我們中斷執行大量寫入的行程(特別是當出現問題時),但是如果你向我們解釋為什么這種行為對你來說是個問題,那么我想我將不得不恢復它。
這就是承認 POSIX 要求write()呼叫是原子的,如果不完整的話,并提供恢復到早期行為的提議,write()在同樣的情況下,呼叫顯然也是完整的。
但請注意,有很多檔案系統不符合 POSIX 標準。
uj5u.com熱心網友回復:
由于檔案描述符參考打開檔案表(系統范圍)中的不同條目,因此它們可能具有不同的檔案偏移量。由于檔案偏移量不同,寫入程序中是否會有競爭條件?
Linux 中的任何 write() 都可以回傳一個短計數,例如由于信號被傳遞到用戶空間處理程式。為簡單起見,讓我們忽略這一點,只考慮成功寫入的資料會發生什么。
有兩種情況:
寫入的區域不重疊。
(例如,一個行程從偏移量 23 開始寫入 100 個位元組,而另一個行程從偏移量 200 開始寫入 50 個位元組。)
在這種情況下沒有競爭條件。
寫入的區域重疊。
(例如,一個行程從偏移量 50 開始寫入 100 個位元組,而另一個行程從偏移量 70 開始寫入 10 個位元組。)
有一個競爭條件。無法預測(沒有咨詢鎖等)資料更新的順序。
取決于目標檔案系統,并且如果寫入足夠大(以便可以觀察到分頁效果),在 Linux 中,在具有多個檔案系統的機器上,這兩個寫入甚至可能“混合”(以頁面大小的塊)硬體執行緒,即使 POSIX 說這不應該發生。
通常,寫入通過 Linux 頁面快取進行。O_DIRECT | O_SYNC其中一個行程可能會繞過頁面快取打開檔案。在這種情況下,可能會出現許多額外的極端情況。具體來說,即使您使用共享時鐘源,并且可以顯示在進行直接寫入呼叫之前正常/頁面快取寫入已完成,頁面快取寫入仍有可能覆寫直接寫入內容。
內核如何避免它?
它沒有。為什么要呢?POSIX 說每次寫入都是原子的,但是沒有實際的方法可以避免僅依賴于它的競爭條件(并獲得一致和預期的結果)。
用戶空間程式至少有四種不同的方法來避免這種競爭:
建議檔案使用flock()介面鎖定整個打開的檔案。
建議檔案使用lockf()介面鎖定整個打開的檔案。在 Linux 中,這些只是在整個檔案上放置/洗掉 fcntl() 咨詢鎖的簡寫。
使用fcntl()介面對檔案進行咨詢記錄鎖定。只要檔案服務器配置為支持檔案鎖定,這甚至可以跨共享卷作業。
使用fcntl()介面獲取打開檔案的獨占租約 。
咨詢檔案鎖就像路燈:它們旨在用于合作流程,以便輕松確定誰可以在什么時候去。但是,它們不會阻止任何其他行程實際忽略“鎖定”并訪問檔案。
檔案租約是一種機制,其中一個或多個行程可以在同一檔案上同時獲得讀租約,但只有一個行程可以獲得寫租約,并且只有當該行程是唯一打開檔案的行程時。授予后,寫租約(或獨占租約)意味著如果任何其他行程試圖打開同一個檔案,租約所有者行程會收到一個信號通知(您可以使用 fcntl() 介面進行控制),并配置時間(通常為 45 秒;參見man 5 proc和/proc/sys/fs/lease-break-time, 以秒為單位) 以放棄租約。打開器在內核中被阻塞,直到租約被降級或租約中斷時間過去,在這種情況下內核會中斷租約。這允許租賃持有人將開業推遲一小段時間。然而,租賃持有者不能阻止打開,并且不能例如用誘餌替換檔案;opener 已經持有 inode,租約中斷時間只是清理作業的寬限期。
從技術上講,第五種方法是強制檔案鎖定,但除了內核使用 wrt。執行的二進制檔案,它們沒有被使用,而且在 Linux 中實際上是錯誤的。在 Linux 中,只有當內核將 inode 作為二進制檔案執行時,inode 才會被鎖定以防修改。(您仍然可以重命名或洗掉原始檔案,然后創建一個新檔案,以便任何后續執行程式將執行修改后的/新資料。嘗試修改作為二進制檔案執行的檔案將失敗并出現錯誤EBUSY。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/414156.html
標籤:
