在Linux中为防止多个进程同时更新文件从而导致数据丢失,或者防止文件内容在未更新完毕时被读取并引发后续问题,需要某种机制,这种机制就是“文件锁”。在linux中多用fcntl()实施文件锁。fcntl()的功能可分为读锁和写锁两种。其中读锁又称为共享锁,它用来防止进程读取的文件记录被更改。记录内可设置多个读锁,但当有一个读锁存在的时候就不能在该记录区域设置写锁。写锁又称为排斥锁,在任何时刻只能有一个程序对文件的记录加写锁,它用来保证文件记录被某一进程更新数据的时候不被其他进程干扰,确保文件数据的正确性,同时也避免其他进程“弄脏”数据。文件记录一旦被设置写锁,就不能再设置任何锁直至该写锁解锁。
fcntl()函数
头文件:#include<unistd.h>
#include<sys/types.h>
#include<fcntl.h>
函数原型:int fcntl(int fd,int cmd,struct flock*lock_set);
函数参数:fd:文件描述符
cmd:检测锁或设置锁,设置情况如下:
1)F_GETLK:检测文件锁状态,检测结果存放在第三个参数的结构体的l_type内
2)F_SETLK:对文件进行锁操作,锁操作类型存放在第三个参数的结构体的l_type内
3) F_SETLKW:同F_SETLK,不过使用该参数时若不能对文件进行锁操作则会阻塞直至可以,(W即wait,等待)
lock_set:结构体类型指针,结构体structflock需要事先设置,与第二个参数连用。
函数返回值:成功:0
失败:-1
注:struct flock成员如下:
structflock
{
shortl_type;
shortl_whence;
off_tl_start;
off_tl_len;
pid_tl_pid;
}
结构体成员说明:
l_type:有三个参数
F_RDLCK:读锁(共享锁)
F_WRLCK:写锁(排斥锁)
F_UNLCK:无锁/解锁
l_whence:相对于偏移量的起点,参数等同于fseek()与lseek()中的whence参数
SEEK_SET:位置为文件开头位置
SEEK_CUR:位置为文件当前读写位置
SEEK_END:位置为文件结尾位置
l_start:加锁区域在文件中的相对位移量,与l_whence的值共同决定加锁区域的起始位置
l_len:加锁区域的长度,若为0则表示直至文件结尾EOF
l_pid:具有阻塞当前进程的锁,其持有的进程号会存放在l_pid中,仅由F_GETLK返回
示例:使用fcntl()函数对文件进行锁操作。当一个进程已经打开文件并加了写锁操作,另一个文件在读写文件时发生阻塞。注:使用两个terminal运行测试程序。
示例程序如下:
/*************************************************************************
@Author: wanghao
@Created Time : Mon 21 May 2018 12:06:10 AMPDT
@File Name: lock.c
@Description:
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
int lock_set(int fd,int type)
{
structflock lock;
lock.l_whence= SEEK_SET;
lock.l_start= 0;
/*Setfull file*/
lock.l_len= 0;
lock.l_type= type;
lock.l_pid= -1;
/*Judgeif it's locked*/
fcntl(fd,F_GETLK,&lock);
/*Ifthe file can't be locked, find out the reason*/
if(lock.l_type!=F_UNLCK)
{
if(lock.l_type==F_RDLCK)
{
printf("Thisis a ReadLock set by %d\n",lock.l_pid);
}
elseif(lock.l_type==F_WRLCK)
{
printf("Thisis a WriteLock set by %d\n",lock.l_pid);
}
}
/*Lockthe file*/
lock.l_type= type;
/*Ifthe file has been locked, the process will wait here*/
if((fcntl(fd,F_SETLKW,&lock))<0)
{
printf("LockFailed:type = %d\n",lock.l_type);
return-1;
}
switch(lock.l_type)
{
caseF_RDLCK:
printf("ReadLockset by %d\n",getpid());break;
caseF_WRLCK:
printf("WriteLockset by %d\n",getpid());break;
caseF_UNLCK:
printf("ReleaseLockby %d\n",getpid());
return1;
break;
}
return0;
}
int main(int argc, const char *argv[])
{
intfd;
if((fd=open("hello.txt",O_RDWR))<0)
{
perror("failto open hello.txt");
exit(0);
}
printf("Thispid_no is %d\n",getpid());
/*Lockfile*/
lock_set(fd,F_WRLCK);
printf("PressENTER to continue...\n");
getchar();
/*Unlockfile*/
lock_set(fd,F_UNLCK);
close(fd);
return0;
}
思考:如何设置该结构体内的成员使得加锁的范围为整个文件?
答案:设置l_whence为SEEK_SET,l_start为0,l_len为0即可。