上一节: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就采用了给文件上锁的方法。