文件鎖
1.fcntl()函數(shù)說明
前面講述的5個基本函數(shù)實現(xiàn)了文件的打開、讀/寫等基本操作,本節(jié)將討論在文件已經(jīng)共享的情況下如何操作,也就是當(dāng)多個用戶共同使用、操作一個文件的情況。這時,Linux通常采用的方法是給文件上鎖,來避免共享的資源產(chǎn)生競爭的狀態(tài)。
文件鎖包括建議性鎖和強(qiáng)制性鎖。建議性鎖要求每個上鎖文件的進(jìn)程都要檢查是否有鎖存在,并且尊重已有的鎖。在一般情況下,內(nèi)核和系統(tǒng)都不使用建議性鎖。強(qiáng)制性鎖是由內(nèi)核執(zhí)行的鎖,當(dāng)一個文件被上鎖進(jìn)行寫入操作時,內(nèi)核將阻止其他任何文件對其進(jìn)行讀寫操作。采用強(qiáng)制性鎖對性能的影響很大,每次讀寫操作都必須檢查是否有鎖存在。
在Linux中,實現(xiàn)文件上鎖的函數(shù)有l(wèi)ockf()和fcntl(),其中l(wèi)ockf()用于對文件施加建議性鎖,而fcntl()不僅可以施加建議性鎖,還可以施加強(qiáng)制性鎖。同時,fcntl()還能對文件的某一記錄上鎖,也就是記錄鎖。
記錄鎖又可分為讀取鎖和寫入鎖,其中讀取鎖又稱為共享鎖,它能夠使多個進(jìn)程都能在文件的同一部分建立讀取鎖。而寫入鎖又稱為排斥鎖,在任何時刻只能有一個進(jìn)程在文件的某個部分建立寫入鎖。當(dāng)然,在文件的同一部分不能同時建立讀取鎖和寫入鎖。
fcntl()函數(shù)具有很豐富的功能,它可以對已打開的文件描述符進(jìn)行各種操作,不僅包括管理文件鎖,還包括獲得設(shè)置文件描述符和文件描述符標(biāo)志、文件描述符的復(fù)制等很多功能。本節(jié)主要介紹fcntl()函數(shù)建立記錄鎖的方法,關(guān)于它的其他操作,感興趣的讀者可以參看fcntl手冊。
2.fcntl()函數(shù)格式
用于建立記錄鎖的fcntl()函數(shù)語法要點(diǎn)如表2.6所示。
表2.6 fcntl()函數(shù)語法要點(diǎn)
所需頭文件 |
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
|
函數(shù)原型 |
int fcntl(int fd, int cmd, struct flock *lock) |
函數(shù)傳入值 |
fd:文件描述符 |
cmd |
F_DUPFD:復(fù)制文件描述符 |
F_GETFD:獲得fd的close-on-exec標(biāo)志,若標(biāo)志未設(shè)置,則文件經(jīng)過exec()函數(shù)之后仍保持打開狀態(tài) |
F_SETFD:設(shè)置close-on-exec標(biāo)志,該標(biāo)志由參數(shù)arg的FD_CLOEXEC位決定 |
F_GETFL:得到open設(shè)置的標(biāo)志 |
F_SETFL:改變open設(shè)置的標(biāo)志 |
F_GETLK:根據(jù)lock描述,決定是否上文件鎖 |
F_SETLK:設(shè)置lock描述的文件鎖 |
F_SETLKW:這是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。
在無法獲取鎖時,會進(jìn)入睡眠狀態(tài);如果可以獲取鎖或者捕捉到信號則會返回
|
lock:結(jié)構(gòu)為flock,設(shè)置記錄鎖的具體狀態(tài),后面會詳細(xì)說明 |
函數(shù)返回值 |
成功:0
1:出錯
|
這里,lock的結(jié)構(gòu)如下所示:
struct flock
{
short l_type;
off_t l_start;
short l_whence;
off_t l_len;
pid_t l_pid;
}
lock結(jié)構(gòu)中每個變量的取值含義如表2.7所示。
表2.7 lock結(jié)構(gòu)變量取值
l_type |
F_RDLCK:讀取鎖(共享鎖) |
F_WRLCK:寫入鎖(排斥鎖) |
F_UNLCK:解鎖 |
l_start |
加鎖區(qū)域在文件中的相對位移量(字節(jié)),與l_whence值一起決定加鎖區(qū)域的起始位置 |
l_whence:
相對位移量的起點(diǎn)(同lseek的whence)
|
SEEK_SET:當(dāng)前位置為文件的開頭,新位置為偏移量的大小 |
SEEK_CUR:當(dāng)前位置為文件指針的位置,新位置為當(dāng)前位置加上偏移量 |
SEEK_END:當(dāng)前位置為文件的結(jié)尾,新位置為文件的大小加上偏移量的大小 |
l_len |
加鎖區(qū)域的長度 |
為加鎖整個文件,通常的方法是將l_start設(shè)置為0,l_whence設(shè)置為SEEK_SET,l_len設(shè)置為0。
3.fcntl()使用實例
下面首先給出了使用fcntl()函數(shù)的文件記錄鎖功能的代碼實現(xiàn)。在該代碼中,首先給flock結(jié)構(gòu)體的對應(yīng)位賦予相應(yīng)的值。
接著調(diào)用兩次fcntl()函數(shù)。用F_GETLK命令判斷是否可以進(jìn)行flock結(jié)構(gòu)所描述的鎖操作:若可以進(jìn)行,則flock結(jié)構(gòu)的l_type會被設(shè)置為F_UNLCK,其他域不變;若不可進(jìn)行,則l_pid被設(shè)置為擁有文件鎖的進(jìn)程號,其他域不變。
用F_SETLK和F_SETLKW命令設(shè)置flock結(jié)構(gòu)所描述的鎖操作,后者是前者的阻塞版。
當(dāng)?shù)谝淮握{(diào)用fcntl()時,使用F_GETLK命令獲得當(dāng)前文件被上鎖的情況,由此可以判斷能不能進(jìn)行上鎖操作;當(dāng)?shù)诙握{(diào)用fcntl()時,使用F_SETLKW命令對指定文件進(jìn)行上鎖/解鎖操作。因為F_SETLKW命令是阻塞式操作,所以,當(dāng)不能把上鎖/解鎖操作進(jìn)行下去時,運(yùn)行會被阻塞,直到能夠進(jìn)行操作為止。
文件記錄鎖的功能代碼具體如下所示:
/* lock_set.c */
int lock_set(int fd, int type)
{
struct flock old_lock, lock;
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 可能已被F_GETLK修改過 */
lock.l_type = type;
/* 根據(jù)不同的type值進(jìn)行阻塞式上鎖或解鎖 */
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());
}
break;
case F_WRLCK:
{
printf("Write lock set by %d\n", getpid());
}
break;
case F_UNLCK:
{
printf("Release lock by %d\n", getpid());
return 1;
}
break;
default:
break;
}/* end of switch */
return 0;
}
下面的實例是文件寫入鎖的測試用例,這里首先創(chuàng)建了一個hello文件,之后對其上寫入鎖,后釋放寫入鎖。代碼如下所示:
/* write_lock.c */
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include "lock_set.c"
int main(void)
{
int fd;
/* 首先打開文件 */
fd = open("hello",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);
}
為了能夠使用多個終端,更好地顯示寫入鎖的作用,本實例主要在PC上測試,讀者可將其交叉編譯,下載到目標(biāo)板上運(yùn)行。下面是在PC上的運(yùn)行結(jié)果。為了使程序有較大的靈活性,筆者采用文件上鎖后由用戶輸入任意鍵使程序繼續(xù)運(yùn)行。建議讀者開啟兩個終端,并且在兩個終端上同時運(yùn)行該程序,以達(dá)到多個進(jìn)程操作一個文件的效果。在這里,筆者首先運(yùn)行終端一,請讀者注意終端二中的第一句。
終端一:
$ ./write_lock
write lock set by 4994
release lock by 4994
終端二:
$ ./write_lock
write lock already set by 4994
write lock set by 4997
release lock by 4997
由此可見,寫入鎖為互斥鎖,同一時刻只能有一個寫入鎖存在。
接下來的程序是文件讀取鎖的測試用例,原理與上面的程序一樣。
/* fcntl_read.c */
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include "lock_set.c"
int main(void)
{
int fd;
fd = open("hello",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);
}
同樣開啟兩個終端,并首先啟動終端一上的程序,其運(yùn)行結(jié)果如下所示。
終端一:
$ ./read_lock
read lock set by 5009
release lock by 5009
終端二:
$ ./read_lock
read lock set by 5010
release lock by 5010
讀者可以將此結(jié)果與寫入鎖的運(yùn)行結(jié)果相比較,可以看出,讀取鎖為共享鎖,當(dāng)進(jìn)程5009已設(shè)置讀取鎖后,進(jìn)程5010仍然可以設(shè)置讀取鎖。
本文選自華清遠(yuǎn)見嵌入式培訓(xùn)教材《從實踐中學(xué)嵌入式Linux應(yīng)用程序開發(fā)》
熱點(diǎn)鏈接:
1、底層文件I/O操作的系統(tǒng)調(diào)用
2、Linux中的文件及文件描述符
3、Linux文件系統(tǒng)之虛擬文件系統(tǒng)(VFS)
4、嵌入式文件系統(tǒng)構(gòu)建
5、Linux系統(tǒng)調(diào)用及用戶編程接口(API)
更多新聞>> |