1. 程式人生 > >建議性鎖和強制性鎖

建議性鎖和強制性鎖

OS AS 建議 內核 operation 應用程序 錯誤碼 同文件 pen

1、鎖的應用場景

假設有一個文件a,它有1000個字節,一個進程A打開a文件並使用lseek定位到文件到末尾的位置,準備寫50個字節,同時進程B也打開這個a文件進行和進程A同樣的操作,那麽文件最後的內容並不是1000+50+50個字節,而是1050,兩個進程後寫入的內容將會覆蓋前面寫的內容,那麽如何解決這種問題呢?

這種情況下,文件鎖應運而生。

2、建議性鎖

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

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

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

多個鎖同時存在的狀態如下:

| LOCK_SH | LOCK_EX

-----------------------------------------------------------------------

LOCK_EX | NO | NO

LOCK_SH | YES | NO

3、建議性鎖的繼承與釋放

flock()根據調用時operation參數傳入LOCK_UN的值來釋放一個文件鎖。

此外,鎖會在相應的文件描述符被關閉之後自動釋放。

如下代碼都會釋放建議性鎖:

flock(fd, LOCK_EX);
flock(fd, LOCK_UN);

flock(fd, LOCK_EX);
fclose(fd);    //這種情況只試用於當前打開的文件只有一個fd指向它。

當一個文件描述符被復制時(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 ()) {
    flock (fd, LOCK_UN);
}

如上這段代碼子進程也會釋放父進程的文件鎖。當然,如果文件描述符設置了close-on-exec標誌且子進程執行了exec(),文件描述符被關閉,子進程不再擁有文件描述符情況除外。

4、建議性鎖的限制

只能對整個文件進行加鎖。這種粗粒度的加鎖會限制協作進程間的並發。假如存在多個進程,其中各個進程都想同時訪問同一個文件的不同部分。

通過flock()只能放置勸告式鎖。

5、強制性鎖

是OS內核的文件鎖。每個對文件操作時,例如執行open、read、write等操作時,OS內部檢測該文件是否被加了強制鎖,如果加鎖導致這些文件操作失敗。也就是內核強制應用程序來遵守遊戲規則;

註意:操作系統默認情況下使用的是建議性鎖,所以在日常開發中執行文件IO先放一把建議性鎖。

建議性鎖和強制性鎖