在Linux系统中,进程运行在自己的虚拟内存空间中,如何协调不同虚拟地址空间中的进程访问一个非共享资源,文件加锁是基本进程通信方法之一。
可以使用fcntl()、lockf()、flock()实现文件锁,进而实现进程之间的通信。
1.fcntl()
【函数介绍】
调用形式(执行失败返回-1):
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock *lock);
fcntl()的功能不只是给文件加锁,也可以修改打开文件的性质。对于给文件增加非强制文件锁时,参数lock指向的结构体flock定义如下:
struct flock{
...
short l_type; //锁类型,可以为:F_RDLOCK/F_WRLOCK/F_UNLOCK, F_RDLOCK/F_WRLOCK为获得文件锁读/写权限,F_UNLOCK为释放文件锁
short l_whence; //l_start的起始点,可以为SEEK_SET,SEEK_CUR,SEEK_END
off_t l_start; //锁的起始偏移值
off_t l_len; //锁定大小
pid_t l_pid;
....
};
该函数返回errno,如果为EAGAIN或者EACCES,表示其他进程已经拥有该文件的锁,本次操作被禁止。其他值的含义略去。
【实例】
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,char* argv[])
{
int fd;
struct flock lock;
int count = 0;
if(argc != 2)
{
printf("Usage: %s filename.\n",argv[1]);
return 1;
}
fd = open(argv[1],O_RDWR);
if(fd<0)
{
printf("Open file failed.\n");
return 1;
}
lock.l_type = F_WRLCK; //设置写锁
lock.l_whence = 0; //从文件起始位置开始
lock.l_start = 0; //偏移值为0
lock.l_len = 0; //整篇加锁
while(fcntl(fd,F_SETLK,&lock)<0)
{
if(errno == EAGAIN||errno == EACCES) //被其他进程加锁了
{
if(++count<5)
{
sleep(1); //加锁申请最多持续5s
}
else
{
fcntl(fd,F_GETLK,&lock); //否则放弃之前打印当前文件被那个进程加了锁,F_GETLK为获得加锁信息
printf("Pid: %ld process find pid %ld process lock the file %s.\n",
(long)getpid(),(long)lock.l_pid,argv[0]);
return 1;
}
}
else
{
printf("Error: exec function fcntl failed.\n");
return 1;
}
}
printf("Pid: %ld process locked the file.\n",(long)getpid());
sleep(8); //占用文件的时间,这里可以是对文件的一些操作
printf("Pid: %ld process release the file.\n",(long)getpid());
return 0;
}
【执行结果】
如果在main函数中进程获得文件锁以后sleep8秒(时间大于另一个进程申请锁的最大时间):
//release after 8 second.
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o filelock systemcall2.c
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ ./filelock testlock.dat& ./filelock testlock.dat& ./filelock testlock.dat &
Pid: 6270 process locked the file.
[1] 6270
[2] 6271
[3] 6272
gaolu@gaolu-desktop:~$ Pid: 6271 process find pid 6270 process lock the file ./filelock.
Pid: 6272 process find pid 6270 process lock the file ./filelock.
Pid: 6270 process release the file.
[1] Done ./filelock testlock.dat
[2]- Exit 1 ./filelock testlock.dat
[3]+ Exit 1 ./filelock testlock.dat
//可见第一个文件占用时间长,后面2个文件无法获得文件锁访问资源
gaolu@gaolu-desktop:~$
修改占用文件(sleep代替)的时间后:
//release after 2 second,eg.
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o filelock systemcall2.c
gaolu@gaolu-desktop:~$ ./filelock testlock.dat& ./filelock testlock.dat& ./filelock testlock.dat &
Pid: 6325 process locked the file.
[1] 6325
[2] 6326
[3] 6327
gaolu@gaolu-desktop:~$ Pid: 6325 process release the file.
Pid: 6326 process locked the file.
Pid: 6326 process release the file.
Pid: 6327 process locked the file.
Pid: 6327 process release the file.
[1] Done ./filelock testlock.dat
[2]- Done ./filelock testlock.dat
[3]+ Done ./filelock testlock.dat
//三个文件依次获得文件的访问权限
gaolu@gaolu-desktop:~$
2.lockf()
【函数介绍】
lockf()是fcntl()在文件加锁方面的一个简化调用,可以方便的进行添加、解除、检测文件锁。
调用形式:
int lockf(int fd,int cmd,off_t len);
cmd为要执行的操作:
------F_LOCK:给文件加互斥锁。如果文件已经被加锁,则进程被阻塞直到另一个进程将锁释放,申请到为止。子进程不会从父进程那里继承锁。可能导致锁的合并。
------F_TLOCK:功能与F_LOCK基本一致,只是当文件已经被加锁时,调用进程不会阻塞而是直接打印错误信息并返回。
------F_ULOCK:对指定的文件部分进行锁的移除。可能导致加锁不分拆分。
------F_TEST:检测是否加锁,返回0表示未加锁或者被当前进程加锁;返回-1表示被其他进程加锁。
【实例】
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,char* argv[])
{
int fd;
int lock_result;
struct flock lock;
if(argc != 2)
{
printf("Usage: %s filename.\n",argv[1]);
return 1;
}
fd = open(argv[1],O_RDWR);
if(fd<0)
{
printf("Open file failed.\n");
return 1;
}
lock_result = lockf(fd,F_LOCK,0); //参数使用F_LOCK,则如果已经加锁,则阻塞到前一个进程释放锁为止,参数0表示对整个文件加锁
if(lock_result<0)
{
perror("Exec lockf function failed.\n");
return 1;
}
printf("Pid: %ld process locked the file.\n",(long)getpid());
sleep(1);
printf("Pid: %ld process release the file.\n",(long)getpid());
return 0;
}
【执行结果】
gaolu@gaolu-desktop:~$
gaolu@gaolu-desktop:~$ gcc -o filelock systemcall2.c
gaolu@gaolu-desktop:~$ ./filelock testlock.dat& ./filelock testlock.dat& ./filelock testlock.dat &
Pid: 6488 process locked the file.
[1] 6488
[2] 6489
[3] 6490
gaolu@gaolu-desktop:~$ Pid: 6488 process release the file.
Pid: 6490 process locked the file. //阻塞到进程6488释放锁的时候申请到
Pid: 6490 process release the file.
Pid: 6489 process locked the file.
Pid: 6489 process release the file. //阻塞到进程6490释放锁的时候申请到锁
[1] Done ./filelock testlock.dat
[2]- Done ./filelock testlock.dat
[3]+ Done ./filelock testlock.dat