1. 程式人生 > >Linux系統檔案I/O程式設計(二)---檔案鎖函式

Linux系統檔案I/O程式設計(二)---檔案鎖函式

檔案鎖

    上一節:http://blog.csdn.net/mybelief321/article/details/8989755講述的5個基本函式函式open()、read()、write()、lseek()和close()實現的檔案的開啟、讀/寫等操作,本節將討論在檔案已經共享的情況下如何操作,也就是當多個使用者共同使用、操作一個檔案的情況。這時,Linux通常採用的方法是給檔案上鎖,來避免共享的資源產生競爭的狀態。

   檔案鎖包括建議性鎖和強制性鎖。建議性鎖要求每個上鎖檔案的程序檢查是否有鎖存在,並且尊重已有的鎖。在一般情況下,核心和系統都不使用建議性鎖。強制性鎖是由核心執行的鎖,當一個檔案被上鎖執行寫入操作時,核心將阻止其他任何檔案對其進行讀寫操作。採用強制性鎖對效能影響很大,每次讀寫都必須檢查是否有鎖存在。

   在Linux中,實行檔案上鎖的函式有lockf()和fcntl(),其中lockf()用於對檔案施加建議性鎖,而fcntl()不僅可以施加建議性鎖,還可以施加強制性鎖。同時,fcntl()還能對檔案的某一記錄上鎖,也就是記錄鎖。

   記錄鎖又可分為讀取鎖和寫入鎖,其中讀取鎖又稱為共享鎖,它能夠使多個程序都能在檔案的同一部分建立讀取鎖。而寫入鎖又稱為排斥鎖,在任何時刻只能有一個程序在某個部分建立寫入鎖。當然,在檔案的同一部分不能同時建立讀取鎖和寫入鎖。

   fcntl()函式具有很豐富的功能,它可以對已開啟的檔案描述符進行各種操作,不僅包括管理檔案鎖,還包括獲得設定檔案描述符和檔案描述符標誌、檔案描述符的複製等很多功能!這一次我先學習一下fcntl()函式建立檔案鎖的方法,關於它的另外的用法...先學會了這個再說吧!

fcntl()函式格式

  

   表1中的lock是一個flock結構體,結構如下:

  

   上圖中的 off_t 就是資料型別 long int ;pid_t 就是資料型別  int,不懂這裡有解釋:點此解釋

   那麼這個結構體lock中每個變數的取值含義如下表2

 

基礎實驗

   本實驗主要是為了練習一下fcntl()函式的檔案記錄鎖的功能。下面首先給出了使用fcntl(0函式的檔案記錄鎖功能的程式碼實現。

   在該程式碼中,首先給flock結構體的對應位賦予相應的值。接著呼叫兩次fcntl()函式,使用F_GETLK命令判斷是否可以進行flock結構體所描述的鎖操作:若可以進行,則flock結構的l_type會被設定為F_UNLCK,其他域不變;若不可進行,則l_pid被設定為擁有檔案鎖的程序號,其他域不變。

   用F_SETLK和F_SETLKW命令設定flock結構所描述的鎖操作,後者是前者的阻塞版。
   當第一次呼叫fcntl()時,使用F_FETLK命令獲得當前檔案被上鎖的情況,由此可以判斷能不能進行上鎖操作;當第二次呼叫fcntl()時,使用F_SETLKW命令對指定檔案進行上鎖/解鎖操作。因為F_SETLKW命令是阻塞式操作,所以,當不能把上鎖/解鎖操作進行下去時,執行會被阻塞,直到能夠進行操作為止。

  本次實驗程式碼均上傳到了網站,請自行下載:點此下載

   檔案記錄鎖的功能程式碼具體如下:

/*lock_set.c*/
int lock_set(int fd,int type)
{
 struct flock old_lock,lock;  /*定義flock結構體*/
 lock.l_whence=SEEK_SET;  /*加鎖整個檔案*/
 lock.l_start=0;
 lock.l_len=0;  
 lock.l_type=type;   
 lock.l_pid=-1;    
 /*判斷檔案是否可以上鎖 */
 fcntl(fd,F_GETLK,&lock);
 if(lock.l_type!=F_UNLCK)
 {
  /*判斷檔案不能上鎖的原因 */
  if(lock.l_type==F_RDLCK) /*該檔案已經有讀取鎖 */
  {
    printf("Read lock already set by %d\n",lock.l_pid);
  } 
  else if(lock.l_type==F_WRLCK) /*該檔案已經有寫入鎖 */
  {
    printf("Write lock already set by %d\n",lock.l_pid);     
  }
 }
 
 /*l_type 可能在執行完上述判斷後被修改了*/
 lock.l_type=type;
 /*根據不同的type值進行阻塞式上鎖或解鎖*/
 if((fcntl(fd,F_SETLKW,&lock))<0)
 {
  printf("Lock failed:type=%d\n",lock.l_type);
  return 1; 
 }
 switch(lock.l_type)
 {
  case F_RDLCK:
  {
   printf("Read lock set by %d\n",getpid());/*getpid()用來得到當前的程序號*/ 
  } 
  break;
  case F_WRLCK:
  {
   printf("Write lock set by %d\n",getpid());
  } 
  break;
  case F_UNLCK:
  {
   printf("Release lock set by %d\n",getpid());
   return 1;
  } 
  break;
  default:break;      
 }  /*end of switch*/
 return 0;
}

    下面的程式碼是檔案寫入鎖的測試用例,這裡首先建立一個hello.c檔案,然後對其上鎖,最後釋放寫入鎖。程式碼如下所示:

/*write_lock.c*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/file.h>
#include"lock_set.c"

int main(void)
{
 int fd;
 
 /*首先開啟檔案*/
 fd=open("/home/song/hello.c",O_RDWR|O_CREAT,0644);
 if(fd<0)
 {
  printf("Open file error!\n");
  exit(1); 
 }
 
 /*給檔案上寫入鎖*/
 lock_set(fd,F_WRLCK);
 getchar();  /*當用戶輸入任意鍵後,程式繼續執行,否則等待*/
 /*給檔案解鎖*/
 lock_set(fd,F_UNLCK);
 getchar();
 close(fd);  /*關閉該檔案*/
 exit(0);
}

     檔案結構如下圖:

    

    使用命令:gcc write_lock.c -o write_lock編譯

   

    為了使程式有較大的靈活性,我們的程式中採用檔案上鎖後由使用者輸入任意鍵使程式繼續執行。為了更好地顯示寫入鎖的作用,在這裡我們開兩個終端,並且在終端上同時執行該程式,以達到多個程序操作一個檔案的效果。首先執行終端1,請注意終端2中的第一句話

    

     

    由上圖可見,寫入鎖為互斥鎖,同一時刻只能有一個寫入鎖存在

    接下來測試檔案讀取鎖,原理和上邊一樣,程式碼如下

/*read_lock.c*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/file.h>
#include"lock_set.c"

int main(void)
{
 int fd;
 
 /*首先開啟檔案*/
 fd=open("/home/song/hello.c",O_RDWR|O_CREAT,0644);
 if(fd<0)
 {
  printf("Open file error!\n");
  exit(1); 
 }
 /*給檔案上讀取鎖*/
 lock_set(fd,F_RDLCK);
 getchar();  /*當用戶輸入任意鍵後,程式繼續執行,否則等待*/
 /*給檔案解鎖*/
 lock_set(fd,F_UNLCK);
 getchar();  /*當用戶輸入任意鍵後,程式繼續執行,否則等待*/  
 close(fd);  /*關閉該檔案,釋放鎖*/
 exit(0); 

}

    在兩個終端下執行的結果如下圖:

   

   

    與寫入鎖的執行結果比較,可有看出,讀取鎖為共享鎖,當程序7170已設定讀取鎖後,程序7294仍然可以設定讀取鎖。

總結:這一節講了檔案鎖的問題,那麼咱們再來想一下:為什麼要有檔案鎖?原因就是當檔案共享,也就是多個使用者共同使用、操作一個檔案的情況,為了避免共享的資源產生競爭的狀態,Linux就採用了給檔案上鎖的方法。