1. 程式人生 > >linux 檔案記錄鎖

linux 檔案記錄鎖

1. 什麼是檔案記錄鎖?

   是對檔案某個範圍的鎖定

2.檔案記錄鎖的功能?

 當一個程序正在讀或者修改檔案的某個部分的時候,它可以阻止其他程序修改同一檔案區

3. 檔案記錄鎖的函式介面?

      fcntl()  此係統呼叫可以用來對已開啟的檔案描述符進行各種控制操作以改變已開啟檔案的各種屬性,根據傳入不同的操作型別命令cmdfcntl會執行不同的操作,fcnt根據cmd不同,接收可變的引數。具體有以下五種型別的操作:

/* Do the file control operation described by CMD on FD. 
   The remaining arguments are interpreted depending on CMD. */  
int fcntl (int __fd, int __cmd, ...);  
  
//根據cmd的不同有以下三種類型的呼叫  
int fcntl(int fd, int cmd);  
int fcntl(int fd, int cmd, long arg);  
int fcntl(int fd, int cmd, struct flock *lock);
/* 
cmd = F_DUPFD,複製一個檔案描述符; 
*/  
int fcntl(int fd, int cmd);  
  
/* 
cmd = F_GETFD,獲得檔案描述符標誌; 
cmd = F_SETFD,設定檔案描述符標誌;arg = 描述符標誌的值,目前只定義了一個標誌: FD_CLOEXEC 
int fcntl(int fd, int cmd); 
int fcntl(int fd, int cmd, long arg); 
*/  
  
/* 
cmd = F_GETFL,獲得檔案狀態標誌; 
cmd = F_SETFL,設定檔案狀態標誌;arg = 狀態標誌的值 
int fcntl(int fd, int cmd); 
int fcntl(int fd, int cmd, long arg); 
*/  
  
/* 
cmd = F_GETOWN,獲得當前接收SIGIO和SIGURG訊號的程序ID或程序組ID 
cmd = F_SETOWN,設定接收SIGIO和SIGURG訊號的程序ID或程序組ID;arg = 程序ID或程序組ID 
int fcntl(int fd, int cmd); 
int fcntl(int fd, int cmd, long arg); 
*/  
  
/* 
Return value: 
 
對於成功的呼叫,根據操作型別cmd不同,有以下幾種情況: 
       F_DUPFD  返回新的檔案描述符 
       F_GETFD  返回檔案描述符標誌 
       F_GETFL  返回檔案狀態標誌 
       F_GETOWN 程序ID或程序組ID 
       All other commands  返回0 
呼叫失敗, 返回-1,並設定errno。 
*/  
上面四個功能都是fcntl提供的很常用的操作,關於記錄鎖的功能就是fcntl提供的第五個功能,具體使用如下:
int fcntl(int fd, int cmd, struct flock *lock);  
  
/* 
cmd = F_GETLK,測試能否建立一把鎖 
cmd = F_SETLK,設定鎖 
cmd = F_SETLKW, 阻塞設定一把鎖 

F_GETLK-判斷由flockptr描述的鎖是否會被另外一把鎖排斥,若存在,則把現存鎖的資訊寫入到flockptr中,若不存在,則除了將l_type設定為F_UNLCK之外,結構中其餘資訊不變;

F_SETLK-設定由flockptr描述的鎖,如果按照共享讀鎖和獨佔寫鎖規則,不允許加鎖,則立即返回錯誤碼;
 -此命令也用於清除flockptr說明的鎖,l_type=F_UNLCK


F_SETLKW-是F_SETLK的阻塞版本,當不滿足加鎖規則時候,程序進入休眠,如果請求建立的鎖已經可用,則休眠由訊號中斷,程序被喚醒;
*/  
需要注意的是,用F_GETLK測試能否建立一把鎖,然後接著用F_SETLK或F_SETLKW企圖建立一把鎖,
由於這兩者不是一個原子操作,所以不能保證兩次fcntl之間不會有另外一個程序插入並建立一把相關的鎖,
從而使一開始的測試情況無效。所以一般不希望上鎖時阻塞,會直接通過呼叫F_SETLK,並對返回結果進行
測試,以判斷是否成功建立所要求的鎖

//POSIX只定義fock結構中必須有以下的資料成員,具體實現可以增加  
struct flock {  
      short l_type;    /* 鎖的型別: F_RDLCK, F_WRLCK, F_UNLCK */  
      short l_whence;  /* 加鎖的起始位置:SEEK_SET, SEEK_CUR, SEEK_END */  
      off_t l_start;   /* 加鎖的起始偏移,相對於l_whence */  
      off_t l_len;     /* 上鎖的位元組數,如果為0,表示從偏移處一直到檔案的末尾*/  
      pid_t l_pid;     /* 已經佔用鎖的PID(只對F_GETLK 命令有效) */  
      /*...*/  
};  

4.  共享讀鎖與獨佔寫鎖:

      多個程序在給定的位元組上可以有一把共享的讀鎖,但是在一個給定位元組上只能有一個程序獨佔一把寫鎖;
如果在一個給定位元組上已經有了一把或者多把讀鎖,可以繼續新增讀鎖,但是不能在該位元組上加寫鎖;如果在一個位元組上已經有了一把獨佔寫鎖,則不能再新增任何讀/寫鎖;
注意:這個規則只適用於不同程序提出的鎖請求,如果單個程序對一個檔案區間已經有了一把鎖,後來又企圖對同一檔案區加另一把鎖,那麼新鎖將替換舊鎖;
5. 記錄鎖的隱含繼承和釋放:(1) 程序終止時,所建立的鎖全部被釋放;
(2) 關閉任何一個描述符,則該程序通過該描述符可以引用的檔案上的任何一把鎖都被釋放;
(3) fork產生的子程序不繼承父程序鎖設定的鎖;
(4) 在執行了exec後,新程式可以繼續執行原來程式的鎖;如果對一個描述符設定了close-on-exec,則exec執行時候,檔案描述符被關閉,對應鎖也被釋放;
6. 在檔案尾加鎖:
(1) 在加鎖之前最好是對檔案當前位置或者當前長度指定一把鎖,因為在lseek和加鎖呼叫之間,可能有另外一個程序在改變該檔案的長度;
(2) 下面程式碼第一次加鎖的時候,得到一把鎖,從檔案尾端開始,包括以後新增的任何資料,都會加鎖;解鎖的時候,檔案尾端已經變化了,所以並沒有解鎖;

7. 強制性鎖和建議性鎖:
合作程序,多個程序都用一致的方式處理記錄鎖,完成某些任務,這些程序集合就是合作程序;
建議性鎖:建議性鎖不做強制檢查鎖規則,需要程序自己獲取,檢查是否有鎖存在,對於合作程序來說,建議性鎖是可行的,但是對於某些非合作程序,他們不程序檢查,就對檔案進行改寫,就會對當前檔案造成影響;如建議性鎖可以比如成紅綠燈,只是建議性的,因為還是有人可以闖紅燈;
強制性鎖:核心對每個open,read,write系統呼叫都進行檢查,檢查訪問的檔案是否違背了鎖規則,如果違背了鎖規則,則該IO呼叫被禁止;

參考部落格:

https://blog.csdn.net/anonymalias/article/details/9197641

https://www.cnblogs.com/wanpengcoder/p/5305749.html