基本进程通信--文件锁

在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
gaolu@gaolu-desktop:~$

你可能感兴趣的:(linux,进程通信,文件锁,lockf(),fcntl())