linux文件鎖flock

在多個進程同時操做同一份文件的過程當中,很容易致使文件中的數據混亂,須要鎖操做來保證數據的完整性,這裏介紹的針對文件的鎖,稱之爲「文件鎖」-flock。

flock,建議性鎖,不具有強制性。一個進程使用flock將文件鎖住,另外一個進程能夠直接操做正在被鎖的文件,修改文件中的數據,緣由在於flock只是用於檢測文件是否被加鎖,針對文件已經被加鎖,另外一個進程寫入數據的狀況,內核不會阻止這個進程的寫入操做,也就是建議性鎖的內核處理策略。

flock主要三種操做類型:
LOCK_SH,共享鎖,多個進程可使用同一把鎖,常被用做讀共享鎖;
LOCK_EX,排他鎖,同時只容許一個進程使用,常被用做寫鎖;
LOCK_UN,釋放鎖;

進程使用flock嘗試鎖文件時,若是文件已經被其餘進程鎖住,進程會被阻塞直到鎖被釋放掉,或者在調用flock的時候,採用LOCK_NB參數,在嘗試鎖住該文件的時候,發現已經被其餘服務鎖住,會返回錯誤,errno錯誤碼爲EWOULDBLOCK。即提供兩種工做模式:阻塞與非阻塞類型。

服務會阻塞等待直到鎖被釋放:
flock(lockfd,LOCK_EX)
服務會返回錯誤發現文件已經被鎖住時:
ret = flock(lockfd,LOCK_EX|LOCK_NB)
同時ret = -1, errno = EWOULDBLOCK 

flock鎖的釋放很是具備特點,便可調用LOCK_UN參數來釋放文件鎖,也能夠經過關閉fd的方式來釋放文件鎖(flock的第一個參數是fd),意味着flock會隨着進程的關閉而被自動釋放掉。

flock其中的一個使用場景爲:檢測進程是否已經存在;git

int checkexit(char* pfile)
{
    if (pfile == NULL)
    {   
        return -1; 
    }   
    int lockfd = open(pfile,O_RDWR);
    if (lockfd == -1) 
    {   
        return -2; 
    }   
    int iret = flock(lockfd,LOCK_EX|LOCK_NB);
    if (iret == -1) 
    {   
        return -3; 
    }   

    return 0;
}

  

 

 

 

1. 場景概述github

在多線程開發中,互斥鎖能夠用於對臨界資源的保護,防止數據的不一致,這是最爲廣泛的使用方法。那在多進程中如何處理文件之間的同步呢?咱們看看下面的圖:多線程

圖中所示的是兩個進程在無同步的狀況下同時更新同一個文件的過程,其主要的操做是:併發

  • 1. 從文件中讀取序號。
  • 2. 使用這個序號完成應用程序定義的任務。
  • 3. 遞增這個序號並將其寫回文件中。

從圖中可得知兩個進程讀取分別增長了所讀取到的序號,並寫回到了文件中,可是若是有相互互斥的話,最後的值應該是1002,而不是所示的1001。爲了防止出現這種狀況,Linux提供了flock(對整個文件加鎖)、fcntl(對整個文件區域加鎖)兩個函數來作進程間的文件同步。同時也可使用信號量來完成所需的同步,但一般使用文件鎖會更好一些,由於內核可以自動將鎖與文件關聯起來。函數

2. flock()spa

flock的聲明以下線程

fcntl()函數提供了比該函數更爲強大的功能,而且所擁有的功能也覆蓋了flock()所擁有的功能,可是在某些應用中任然使用着flock()函數,而且在繼承和鎖釋放方面的一些語義 中flock()與fcntl()仍是有所不一樣的。code

flock()系統調用是在整個文件中加鎖,經過對傳入的fd所指向的文件進行操做,而後在經過operation參數所設置的值來肯定作什麼樣的操做。operation能夠賦以下值:blog

在默認狀況下,若是另外一個進程已經持有了文件上的一個不兼容的鎖,那麼flock()會阻塞。若是須要防止這種狀況的出現,能夠在operation參數中對這些值取OR(|)。在這種狀況下,若是一個進程已經持有了文件上的一個不兼容鎖,那麼flock()就會阻塞,相反,它會返回-1,並將errno設置成EWOULDBLOCK。繼承

任意數量的進程可同時持有一個文件上的共享鎖,但子任意時刻只能有一個進程可以持有一個文件上的互斥鎖,(這有點相似讀寫鎖)。下圖是進程A先設置了鎖,進程B後設置鎖的支持狀況:

不管程序以什麼模式打開了文件(讀、寫或者讀寫),該文件上均可以放置一把共享鎖或互斥鎖。在實際操做過程當中,參數operation能夠指定對應的值將共享鎖轉換成互斥鎖(反之亦然)。將一個共享鎖轉換成互斥鎖,若是另外一個進程要獲取該文件的共享鎖則會阻塞,除非operation參數指定了LOCK_NB標記,即:(LOCK_SH | LOCK_NB)。鎖的轉換過程不是一個原子操做,在轉換的過程當中首先會刪除既有的鎖,而後建立新鎖。

3. 鎖繼承與釋放的語義

flock()根據調用時operation參數傳入LOCK_UN的值來釋放一個文件鎖。此外,鎖會在相應的文件描述符被關閉以後自動釋放。同時,當一個文件描述符被複制時(dup()、dup2()、或一個fcntl() F_DUPFD操做),新的文件描述符會引用同一個文件鎖。

這段代碼先在fd上設置一個互斥鎖,而後經過fd建立一個指向相同文件的新文件描述符new_fd,最後經過new_fd來解鎖。從而咱們能夠得知新的文件描述符指向了同一個鎖。因此,若是經過一個特定的文件描述符獲取了一個鎖而且建立了該描述符的一個或多個副本,那麼,若是不顯示的調用一個解鎖操做,只有當文件描述符副本都被關閉了以後鎖纔會被釋放。

由上咱們能夠推出,若是使用fork()建立一個子進程,子進程會複製父進程中的全部描述符,從而使得它們也會指向同一個文件鎖。例以下面的代碼會致使一個子進程刪除一個父進程的鎖:

因此,有時候能夠利用這些語義來將一個文件鎖從父進程傳輸到子進程:在fork()以後,父進程關閉其文件描述符,而後鎖就只在子進程的控制之下了。經過fork()建立的鎖在exec()中會得以保留(除非在文件描述符上設置了close-on-exec標記而且該文件描述符是最後一個引用底層的打開文件描述的描述符)。

若是程序中使用open()來獲取第二個引用同一個文件的描述符,那麼,flock()會將其視爲不一樣的文件描述符。以下代碼會在第二個flock()上阻塞。

4. flock()的限制

flock()放置的鎖有以下限制

  • 只能對整個文件進行加鎖。這種粗粒度的加鎖會限制協做進程間的併發。假如存在多個進程,其中各個進程都想同時訪問同一個文件的不一樣部分。
  • 經過flock()只能放置勸告式鎖。
  • 不少NFS實現不識別flock()放置的鎖。

註釋:在默認狀況下,文件鎖是勸告式的,這表示一個進程能夠簡單地忽略另外一個進程在文件上放置的鎖。要使得勸告式加鎖模型可以正常工做,全部訪問文件的進程都必需要配合,即在執行文件IO以前先放置一把鎖。

相關文章
相關標籤/搜索