当多个用户共同使用、操作一个文件的情况下,这时,Linux通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。
文件锁包括建议性锁和强制性锁。建议性锁要求每个上锁文件的进程都要检查是否有锁存在,并且尊重己有的锁。在一般情况下,内核和系统都不使用建议性锁。强制性锁是由内核执行的锁,当文件上锁进行写入操作时,内核将阻止其他任何文件对其进行读写操作。采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。
在Linux中,实现文件上锁的函数有flock()和fcntl(),其中flock()用于对文件施加建议性锁,而fcntl()不仅可以施加建议性锁,还可以施加强制性锁,还能对文件的某一记录进行上锁,也就是记录锁。
记录锁又分为读取锁和写入锁。读取锁又称共享锁,能使多个进程都在文件的同一部分建立读取锁。写入锁又称为排斥锁,在任何时刻只能有一个进程在文件的某个部分建立写入锁。在文件的同一部分不能同时建立读取锁和写入锁。
fcntl函数:int fcntl(int filedes, int cmd, .../*int arg */); --------- int fcntl(intfd,cmd,struct flock*lock)
返回:若成功则依赖于cmd,若出错为-1。
作用:可以改变已打开的文件的性质
下列三个命令有特定的返回值:F_DUPFD,F_GETFD以及F_GETOWN。
第一个返回新的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。
第三个参数总是一个整数,当然在作为记录锁用时,第三个参数则是指向一个结构的指针。
fcntl函数有五种功能:
(1)复制一个现存的描述符(cmd=F_DUPFD)
(2)获得/设置文件描述符标志(cmd=F_GETFD或F_SETFD)
(3)获得/设置文件状态标志(cmd=F_GETFL或F_SETFL)
(4)获得/设置异步I/O有权(cmd=F_GETOWN或F_SETOWN)
(5)获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)
F_DUPFD:复制文件描述符filedes,新文件描述符作为函数值返回。它是尚未打开的各描述符中大于或等于第三个参数值中各值的最小值。新描述符与filedes共享同一文件表项。但是新描述符有它自己的一套文件描述符标志,其FD_CLOEXEC文件描述符标志被清除。
F_GETFD:对应于filedes的文件描述符标志作为函数值返回。当前只定义了一个文件描述符标志FD_CLOEXEC。
F_SETFD:对于filedes设置文件描述符标志。新标志值按第三个参数设置。
F_GETFL:对应于filedes的文件状态标志作为函数返回。在说明open函数时,已说明了文件状态标志。
F_SETFL:将文件状态标志设置为第三个参数的值(取整数值),可以更改的几个标志:O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC。
F_GETOWN:取当前接受SIGIO和SIGURG信号的进程ID或进程组ID。
F_SETOWN:设置接受SIGIO和SIGURG信号的进程ID或进程组ID。正的arg指定一个进程ID,负的arg表示等于arg绝对值的一个进程组ID。
F_GETFK:根据lock描述,决定是否上文件锁
F_SETFK:设置lock描述的文件锁
F_SETLKW:这是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。如果存在其它锁,则调用进程睡眠;如果捕捉到信号则睡眠中断
lock:结构为flock,记录锁的具体状态
struct flock { short l_type; off_t l_start; short l_whence; off_t l_len; pid_t l_pid; }
Lock结构变量取值:
l_type: F_RDLCK:读取锁(共享锁)
F_WRLCK:写入锁(排斥锁)
F_UNLCK:解锁
l_start: 相对位移量(字节)
l_whence: 相对位移量的起点(同lseek的whence):
SEEK_SET:当前位置为文件开头,新位置为偏移量的大小
SEEK_CUR:当前位置为文件指针位置,新位置为当前位置加上偏移量
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量大小
l_len: 加锁区域长度
为了锁定整个文件,通常的做法是将l_start设置为0,l_whence设置为SEEK_SET,l_len设置为0
范例:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <fcntl.h> 5 int main(int argc, char **argv) 6 { 7 int fd; 8 int var; 9 if(argc!=2) 10 { 11 printf("please input filename and one arg! "); 12 exit(1); 13 } 14 if((var=fcntl(atoi(argv[1]),F_GETFL,0))==-1) 15 { 16 printf("fcntl file error! "); 17 exit(1); 18 } 19 printf("%d ",var); 20 switch(var & O_ACCMODE) 21 { 22 case O_RDONLY:printf("Read only. "); 23 break; 24 case O_WRONLY:printf("Write only. "); 25 break; 26 case O_RDWR: printf("Read Write. "); 27 break; 28 default:break; 29 } 30 exit(0); 31 }
-2
1 int lock_set(int fd,int type) 2 { 3 struct flock lock; 4 lock.l_type=type; 5 lock.l_start=0; 6 lock.l_whence=SEEK_SET; 7 lock.l_len=0; 8 lock.l_pid=-1; 9 fcntl(fd,F_GETLK,&lock); 10 if(lock.l_type!=F_UNLCK) 11 { 12 13 if(lock.l_type==F_RDLCK) //该文件已有读取锁 14 { 15 printf("Read lock already set by %d ",lock.l_pid); 16 } 17 elseif(lock.l_type==F_WRLCK) //该文件已有写入锁 18 { 19 printf("Write lock already set by %d ",lock.l_pid); 20 } 21 } 22 23 lock.l_type=type; 24 25 if((fcntl(fd,F_SETLKW,&lock))<0) 26 { 27 printf("Lockfailed:type=%d ",lock.l_type); 28 return1; 29 } 30 switch(lock.l_type) 31 { 32 caseF_RDLCK: 33 { 34 printf("Read lock set by %d ",getpid()); 35 } 36 break; 37 case F_WRLCK: 38 { 39 printf("Write lock set by %d ",getpid()); 40 } 41 break; 42 case F_UNLCK: 43 { 44 printf("Release lock by %d ",getpid()); 45 return 1; 46 } 47 break; 48 default: 49 break; 50 } 51 return 0; 52 } 53 54 #include<unistd.h> 55 #include<stdio.h> 56 #include<stdlib.h> 57 #include<sys/types.h> 58 #include<sys/stat.h> 59 #include<sys/file.h> 60 #include "lock_set.c" 61 int main(void) 62 { 63 int fd; 64 65 fd=open("hello",O_RDWR|O_CREAT,0644); 66 if(fd<0) 67 { 68 printf("Openfile error!! "); 69 exit(1); 70 } 71 72 lock_set(fd,F_WRLCK); 73 getchar(); 74 75 lock_set(fd,F_UNLCK); 76 getchar(); 77 close(fd); 78 exit(0); 79 }