上一節: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就采用了給文 件上鎖的方法。