在多執行緒開發中,互斥鎖可以用於對臨界資源的保護,防止資料的不一致,這是最為普遍的使用方法。那在多程序中如何處理檔案之間的同步呢?我們看看下面的圖:
圖中所示的是兩個程序在無同步的情況下同時更新同乙個檔案的過程,其主要的操作是:
1. 從檔案中讀取序號。
2. 使用這個序號完成應用程式定義的任務。
3. 遞增這個序號並將其寫回檔案中。
從圖中可得知兩個程序讀取分別增加了所讀取到的序號,並寫回到了檔案中,但是如果有相互互斥的話,最後的值應該是1002,而不是所示的1001。為了防止出現這種情況,linux提供了flock(對整個檔案加鎖)、fcntl(對整個檔案區域加鎖)兩個函式來做程序間的檔案同步。同時也可以使用訊號量來完成所需的同步,但通常使用檔案鎖會更好一些,因為核心能夠自動將鎖與檔案關聯起來。
flock的宣告如下
#include
// returns 0 on success, or -1 on error
int flock (intfd, int operation);
fcntl()函式提供了比該函式更為強大的功能,並且所擁有的功能也覆蓋了flock()所擁有的功能,但是在某些應用中任然使用著flock()函式,並且在繼承和鎖釋放方面的一些語義 中flock()與fcntl()還是有所不同的。
flock()系統呼叫是在整個檔案中加鎖,通過對傳入的fd所指向的檔案進行操作,然後在通過operation引數所設定的值來確定做什麼樣的操作。operation可以賦如下值:
在預設情況下,如果另乙個程序已經持有了檔案上的乙個不相容的鎖,那麼flock()會阻塞。如果需要防止這種情況的出現,可以在operation引數中對這些值取or(|)。在這種情況下,如果乙個程序已經持有了檔案上的乙個不相容鎖,那麼flock()就會阻塞,相反,它會返回-1,並將errno設定成ewouldblock。
任意數量的程序可同時持有乙個檔案上的共享鎖,但子任意時刻只能有乙個程序能夠持有乙個檔案上的互斥鎖,(這有點類似讀寫鎖)。下圖是程序a先設定了鎖,程序b後設定鎖的支援情況:
無論程式以什麼模式開啟了檔案(讀、寫或者讀寫),該檔案上都可以放置一把共享鎖或互斥鎖。在實際操作過程中,引數operation可以指定對應的值將共享鎖轉換成互斥鎖(反之亦然)。將乙個共享鎖轉換成互斥鎖,如果另乙個程序要獲取該檔案的共享鎖則會阻塞,除非operation引數指定了lock_nb標記,即:(lock_sh | lock_nb)。鎖的轉換過程不是乙個原子操作,在轉換的過程中首先會刪除既有的鎖,然後建立新鎖。
flock()根據呼叫時operation引數傳入lock_un的值來釋放乙個檔案鎖。此外,鎖會在相應的檔案描述符被關閉之後自動釋放。同時,當乙個檔案描述符被複製時(dup()、dup2()、或乙個fcntl() f_dupfd操作),新的檔案描述符會引用同乙個檔案鎖。
flock(fd, lock_ex);
new_fd = dup(fd);
flock(new_fd, lock_un);
這段**先在fd上設定乙個互斥鎖,然後通過fd建立乙個指向相同檔案的新檔案描述符new_fd,最後通過new_fd來解鎖。從而我們可以得知新的檔案描述符指向了同乙個鎖。所以,如果通過乙個特定的檔案描述符獲取了乙個鎖並且建立了該描述符的乙個或多個副本,那麼,如果不顯示的呼叫乙個解鎖操作,只有當檔案描述符副本都被關閉了之後鎖才會被釋放。
由上我們可以推出,如果使用fork()建立乙個子程序,子程序會複製父程序中的所有描述符,從而使得它們也會指向同乙個檔案鎖。例如下面的**會導致乙個子程序刪除乙個父程序的鎖:
flock (fd, lock_ex);
if (0 == fork ())
所以,有時候可以利用這些語義來將乙個檔案鎖從父程序傳輸到子程序:在fork()之後,父程序關閉其檔案描述符,然後鎖就只在子程序的控制之下了。通過fork()建立的鎖在exec()中會得以保留(除非在檔案描述符上設定了close-on-exec標記並且該檔案描述符是最後乙個引用底層的開啟檔案描述的描述符)。
如果程式中使用open()來獲取第二個引用同乙個檔案的描述符,那麼,flock()會將其視為不同的檔案描述符。如下**會在第二個flock()上阻塞。
fd1 = open ("test.txt", o_rdwd);
fd2 = open ("test.txt", o_rdwd);
flock (fd1, lock_ex);
flock (fd2, lock_ex);
flock()放置的鎖有如下限制
只能對整個檔案進行加鎖。這種粗粒度的加鎖會限制協作程序間的併發。假如存在多個程序,其中各個程序都想同時訪問同乙個檔案的不同部分。
通過flock()只能放置勸告式鎖。
很多nfs實現不識別flock()放置的鎖。
注釋:在預設情況下,檔案鎖是勸告式的,這表示乙個程序可以簡單地忽略另乙個程序在檔案上放置的鎖。要使得勸告式加鎖模型能夠正常工作,所有訪問檔案的程序都必須要配合,即在執行檔案io之前先放置一把鎖。
linux 中的檔案鎖
linux執行多個程序同時對一檔案進行讀寫,雖然每乙個read和write都是原子操作,但核心並沒有在兩個讀寫操作之間加以同步。因此,當乙個程序多次呼叫read讀檔案時,就有可能在某兩次讀之間被另一程序所寫,因此,讀的的值將發生錯誤,造成了檔案資料的隨機性衝突,為解決此類併發程序對共享檔案的訪問控制...
UNIX LINUX程式設計學習之檔案鎖 記錄鎖
鎖定中的幾個概念 檔案鎖定的是整個檔案,而記錄鎖定只鎖定檔案的某一特定部分。unix 的記錄指的是從檔案的某一相對位置開始的一段連續的位元組流,它不同於其它以強制性記錄結構阻止檔案的作業系統,因此,unix 記錄鎖更恰當的稱呼應該是範圍鎖,它是對檔案某個範圍的鎖定。檔案和記錄鎖定可分為諮詢式鎖定和強...
linux中的fctnl檔案鎖
linux中fctnl檔案鎖 核心2.4.21 fctnl可以實現對檔案進行加鎖,保證多程序對同一檔案操作的正確性。下面是乙個簡單封裝的檔案加鎖函式 int start lock file int fd,int write lock,int wait lock else slock.l whence...