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就採用了給檔案上鎖的方法。