對文件加鎖是原子性的,可以用于進程間文件操作的同步。在linux下,有三個函數(shù)可以對文件進程加鎖,分別是fcntl、flock、lockf。這里只說fcntl,它的用法也是最復雜的。
fcntl是file control的縮寫。在linux下大部分設(shè)備都是文件,所以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。建議性鎖是指給文件上鎖后,只在文件上設(shè)置了一個鎖的標識。其他進程在對這個文件進程操作時,可以檢測到鎖的存在,但這個鎖并不能阻止它對這個文件進行操作。這就好比紅綠燈,當亮紅燈時,告訴你不要過馬路,但如果你一定要過,也攔不住你。強制性鎖則是當給文件上鎖后,當其他進程要對這個文件進程不兼容的操作(如上了讀鎖,另一個進程要寫),則系統(tǒng)內(nèi)核將阻塞后來的進程直到第一個進程將鎖解開。在該功能下,fcntl的函數(shù)原型為:
#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(xiàn)_SETLK,只是操作變成阻塞式的。而fcntl可以用過l_whence、l_start、l_len來控制文件上鎖的區(qū)間。下面分別是上鎖、測試鎖的代碼。
/* 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多少字節(jié)開始加鎖,"_lock.l_len = 0;"表示加鎖的字節(jié)數(shù),即長度(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再運行g(shù)lock:
./slock
sleep now ...
./glock
lock is 1
exit...
slock先給文件上寫鎖,然后glock測試讀共享鎖是否能加上,測試結(jié)果是已存在一個寫鎖(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來判斷文件是否上鎖,只能測試某個鎖是否能加上。
上面的是建議性鎖,如果要實現(xiàn)強制性鎖,則:
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)).
這是說,要實現(xiàn)強制性鎖則須將文件所在的文件系統(tǒng)用"-o mand"參數(shù)來掛載,并且使用chmod函數(shù)將文件用戶組的x權(quán)限去掉。然后用上面同樣的代碼就可以了。我第一次見這么奇特的函數(shù),實現(xiàn)一個功能并不是通過本身的參數(shù)控制,而是系統(tǒng)設(shè)置.....幸好我也不用強制性鎖。
以上是fcntl加文件鎖的簡單例子。需要注意的是不同系統(tǒng)的實現(xiàn)并不一樣,宏定義也不一樣。如:
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. */