Linux系统文件I/O编程(二)---文件锁函数

来源:互联网 发布:办公网络服务器 编辑:程序博客网 时间:2024/05/22 08:03

文件锁

    上一节:http://blog.csdn.net/mybelief321/article/details/8989755讲述的5个基本函数函数open()、read()、write()、lseek()和close()实现的文件的打开、读/写等操作,本节将讨论在文件已经共享的情况下如何操作,也就是当多个用户共同使用、操作一个文件的情况。这时,Linux通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。

   文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程检查是否有锁存在,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当一个文件被上锁执行写入操作时,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能影响很大,每次读写都必须检查是否有锁存在。

   在Linux中,实行文件上锁的函数有lockf()和fcntl(),其中lockf()用于对文件施加建议性锁,而fcntl()不仅可以施加建议性锁,还可以施加强制性锁。同时,fcntl()还能对文件的某一记录上锁,也就是记录锁。

   记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁。而写入锁又称为排斥锁,在任何时刻只能有一个进程在某个部分建立写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。

   fcntl()函数具有很丰富的功能,它可以对已打开的文件描述符进行各种操作,不仅包括管理文件锁,还包括获得设置文件描述符和文件描述符标志、文件描述符的复制等很多功能!这一次我先学习一下fcntl()函数建立文件锁的方法,关于它的另外的用法...先学会了这个再说吧!

fcntl()函数格式

  

   表1中的lock是一个flock结构体,结构如下:

  

   上图中的 off_t 就是数据类型 long int ;pid_t 就是数据类型  int,不懂这里有解释:点此解释

   那么这个结构体lock中每个变量的取值含义如下表2

 

基础实验

   本实验主要是为了练习一下fcntl()函数的文件记录锁的功能。下面首先给出了使用fcntl(0函数的文件记录锁功能的代码实现。

   在该代码中,首先给flock结构体的对应位赋予相应的值。接着调用两次fcntl()函数,使用F_GETLK命令判断是否可以进行flock结构体所描述的锁操作:若可以进行,则flock结构的l_type会被设置为F_UNLCK,其他域不变;若不可进行,则l_pid被设置为拥有文件锁的进程号,其他域不变。

   用F_SETLK和F_SETLKW命令设置flock结构所描述的锁操作,后者是前者的阻塞版。
   当第一次调用fcntl()时,使用F_FETLK命令获得当前文件被上锁的情况,由此可以判断能不能进行上锁操作;当第二次调用fcntl()时,使用F_SETLKW命令对指定文件进行上锁/解锁操作。因为F_SETLKW命令是阻塞式操作,所以,当不能把上锁/解锁操作进行下去时,运行会被阻塞,直到能够进行操作为止。

  本次实验代码均上传到了网站,请自行下载:点此下载

   文件记录锁的功能代码具体如下:

/*lock_set.c*/
int lock_set(int fd,int type)
{
 struct flock old_lock,lock;  /*定义flock结构体*/
 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 可能在执行完上述判断后被修改了*/
 lock.l_type=type;
 /*根据不同的type值进行阻塞式上锁或解锁*/
 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());/*getpid()用来得到当前的进程号*/ 
  } 
  break;
  case F_WRLCK:
  {
   printf("Write lock set by %d\n",getpid());
  } 
  break;
  case F_UNLCK:
  {
   printf("Release lock set by %d\n",getpid());
   return 1;
  } 
  break;
  default:break;      
 }  /*end of switch*/
 return 0;
}

    下面的代码是文件写入锁的测试用例,这里首先创建一个hello.c文件,然后对其上锁,最后释放写入锁。代码如下所示:

/*write_lock.c*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/file.h>
#include"lock_set.c"

int main(void)
{
 int fd;
 
 /*首先打开文件*/
 fd=open("/home/song/hello.c",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);
}

     文件结构如下图:

    

    使用命令:gcc write_lock.c -o write_lock编译

   

    为了使程序有较大的灵活性,我们的程序中采用文件上锁后由用户输入任意键使程序继续执行。为了更好地显示写入锁的作用,在这里我们开两个终端,并且在终端上同时运行该程序,以达到多个进程操作一个文件的效果。首先运行终端1,请注意终端2中的第一句话

    

     

    由上图可见,写入锁为互斥锁,同一时刻只能有一个写入锁存在

    接下来测试文件读取锁,原理和上边一样,代码如下

/*read_lock.c*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/file.h>
#include"lock_set.c"

int main(void)
{
 int fd;
 
 /*首先打开文件*/
 fd=open("/home/song/hello.c",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); 

}

    在两个终端下运行的结果如下图:

   

   

    与写入锁的运行结果比较,可有看出,读取锁为共享锁,当进程7170已设置读取锁后,进程7294仍然可以设置读取锁。

 

总结:这一节讲了文件锁的问题,那么咱们再来想一下:为什么要有文件锁?原因就是当文件共享,也就是多个用户共同使用、操作一个文件的情况,为了避免共享的资源产生竞争的状态,Linux就采用了给文件上锁的方法。

 

原创粉丝点击