對文件加鎖是原子性的,可以用於進程間文件操作的同步。在linux下,有三個函數可以對文件進程加鎖,分別是fcntl、flock、lockf。這裡只說fcntl,它的用法也是最復雜的。
fcntl是file control的縮寫。在linux下大部分設備都是文件,所以fcntl的功能也比較多,包括:
•Duplicating a file descriptor(復制文件描述符)
•File descriptor flags(操作close-on-exec標志)
•File status flags(操作文件O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC標識)
•Advisory locking(建議性鎖)
•Mandatory locking(強制性鎖)
•Managing signals(管理信號)
•Leases(租借鎖)
•File and directory change notification (dnotify)(文件和目錄更改消息)
•Changing the capacity of a pipe(改變管道大小)
這裡只說一下Advisory locking和Mandatory locking。建議性鎖是指給文件上鎖後,只在文件上設置了一個鎖的標識。其他進程在對這個文件進程操作時,可以檢測到鎖的存在,但這個鎖並不能阻止它對這個文件進行操作。這就好比紅綠燈,當亮紅燈時,告訴你不要過馬路,但如果你一定要過,也攔不住你。強制性鎖則是當給文件上鎖後,當其他進程要對這個文件進程不兼容的操作(如上了讀鎖,另一個進程要寫),則系統內核將阻塞後來的進程直到第一個進程將鎖解開。在該功能下,fcntl的函數原型為:
復制代碼代碼如下:
#include <unistd.h>
#include <fcntl.h></p>
<p>int fcntl(int fd, int cmd,struct flock *plock );</p>
<p>struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(F_GETLK only) */
...
};
Advisory locking共有三個操作,分別是F_GETLK、F_SETLK、F_SETLKW。其中F_GETLK用來測試鎖,注意是測試而不是獲取鎖;F_SETLK用來加鎖、解鎖;F_SETLKW功能同F_SETLK,只是操作變成阻塞式的。而fcntl可以用過l_whence、l_start、l_len來控制文件上鎖的區間。下面分別是上鎖、測試鎖的代碼。
復制代碼代碼如下:
/* slock.c */</p>
<p>#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h></p>
<p>int main()
{
struct flock _lock;</p>
<p> _lock.l_type = F_WRLCK;
_lock.l_whence = SEEK_SET;
_lock.l_start = 0;
_lock.l_len = 0;</p>
<p> int fd = open( "/dev/shm/test",O_CREAT|O_RDWR,S_IRWXU|S_IRGRP|S_IWGRP|S_IRWXO );
if ( fd < 0 )
{
puts( "open error" );
return 0;
}</p>
<p> int ret = fcntl( fd,F_SETLK,&_lock );
if ( ret < 0 )
{
puts( "fcntl error" );
close( fd );
return 0;
}</p>
<p> puts( "sleep now ..." );
sleep( 100 );
puts( "exit..." );
_lock.l_type = F_UNLCK;
_lock.l_whence = SEEK_SET;
_lock.l_start = 0;
_lock.l_len = 0;</p>
<p> ret = fcntl( fd,F_SETLK,&_lock );
if ( ret < 0 )
{
puts( "unlock error" );
}</p>
<p> close( fd );
}
復制代碼代碼如下:
/* glock.c */</p>
<p>#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h></p>
<p>int main()
{
struct flock _lock;</p>
<p> _lock.l_type = F_RDLCK;
_lock.l_whence = SEEK_SET;
_lock.l_start = 0;
_lock.l_len = 0;</p>
<p> int fd = open( "/dev/shm/test",O_RDWR );
if ( fd < 0 )
{
perror( "open error" );
return 0;
}</p>
<p> int ret = fcntl( fd,F_GETLK,&_lock );
if ( ret < 0 )
{
perror( "fcntl error:" );
close( fd );
return 0;
}</p>
<p> printf( "lock is %d\n",_lock.l_type );</p>
<p> close( fd );
}
在上面的代碼中,"_lock.l_type = F_RDLCK;"表示給文件上讀共享鎖,"_lock.l_whence = SEEK_SET;"表示從文件開頭開始加鎖,"_lock.l_start = 0;"表示偏移l_whence多少字節開始加鎖,"_lock.l_len = 0;"表示加鎖的字節數,即長度(Specifying 0 for l_len has the special meaning: lock all bytes starting at the location specified by l_whence and l_start through to the end of file, no matter how large the file grows.)。
在上面的代碼中,分別編譯為slock、glock。先運行slock再運行glock:
復制代碼代碼如下:
./slock
sleep now ...
./glock
lock is 1
exit...
slock先給文件上寫鎖,然後glock測試讀共享鎖是否能加上,測試結果是已存在一個寫鎖(F_WRLCK,debian下定義為1)。這裡需要注意的是F_GETLK是測試鎖是否能加上,如果可以,則struct flock中的l_type為F_UNLCK;如果不行,則l_type為文件當前鎖的類型,而l_pid為上鎖的進程pid。故如果slock上的鎖是F_RDLCK,glock測試的鎖也是F_RDLCK,這兩個鎖是兼容的,返回的l_type類型為F_UNLCK。即你不能通過F_GETLK來判斷文件是否上鎖,只能測試某個鎖是否能加上。
上面的是建議性鎖,如果要實現強制性鎖,則:
復制代碼代碼如下:
To make use of mandatory locks, mandatory locking must be enabled both on the filesystem that contains the file to be locked, and on the file itself. Mandatory locking is enabled on a filesystem using the "-o
mand" option to mount(8), or the MS_MANDLOCK flag for mount(2). Mandatory locking is enabled on a file by disabling group execute permission
on the file and enabling the set-group-ID permission bit (see chmod(1) and chmod(2)).
這是說,要實現強制性鎖則須將文件所在的文件系統用"-o mand"參數來掛載,並且使用chmod函數將文件用戶組的x權限去掉。然後用上面同樣的代碼就可以了。我第一次見這麼奇特的函數,實現一個功能並不是通過本身的參數控制,而是系統設置.....幸好我也不用強制性鎖。
以上是fcntl加文件鎖的簡單例子。需要注意的是不同系統的實現並不一樣,宏定義也不一樣。如:
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/fcntl.h
/* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
#define F_RDLCK 1 /* shared or read lock */
#define F_UNLCK 2 /* unlock */
#define F_WRLCK 3 /* exclusive or write lock */
而在debian中,/usr/include/bits/fcntl.h
/* For posix fcntl() and `l_type' field of a `struct flock' for lockf(). */
#define F_RDLCK 0 /* Read lock. */
#define F_WRLCK 1 /* Write lock. */
#define F_UNLCK 2 /* Remove lock. */