linux下文件锁定有两种:一种是以原子操作方式创建锁文件;另一种是允许锁定文件的一部分,从而独享对这一部分内容的访问。
1、锁文件
许多应用程序只需要能够针对某个资源创建一个锁文件,然后其他程序通过检查这个文件来判断它们是否被允许访问这个资源。创建锁文件使用fcntl.h头文件定义的open系统调用,并带上O_CREAT和O_EXCL标志。这样就以原子操作完成两项工作:确定文件不存在,然后创建
2、区域锁定
区域锁定出现,是因为锁文件方式并不适用于访问大型的共享文件。如果一个大文件,由一个程序写入数据,但却由不同的程序同时对这个文件进行更新。处理程序不能等待记录程序结束,所以需要一些协调方法来提供对同一个文件的并发访问。linux提供了2种方法来实现:一是fcntl系统调用和lockf调用。
fcntl系统调用,它的定义如下:
#include<fcntl.h> int fcntl(int fildes , int command , ...);fcntl对一个打开的文件描述符进行操作,并能根据commands参数设置完成不同的任务。它为我们提供了三个用于文件锁定的命令选项。
F_GETLK
F_SETLK
F_SETLKW
当使用这个命令选项时,fcntl的第三个参数必须是一个指向flock结构的指针,所以实际的函数原型为:
int fcntl(int fildes , int command , struct flock *flock_structure);
flock(文件锁)结构依赖具体的实现,但它至少包含下述成员:
short l_type;
short l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
l_type成员的取值定义在头文件fcntl.h中,
F_RDLCK 共享(或读)锁
F_UNLCK 解锁,用来清除锁
F_WRLCK 独占(或写)锁
更具体的详情请查看LINUX程序设计P226
一个使用fcntl锁定文件的例子如下:
F_GETLK、F_SETLK、F_SETLKW,l_type提供的选项有F_RDLCK、F_UNLCK、F_WRLCK,分别为读锁,解锁,写锁
测试锁的程序如下:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> const char *test_file = "/tmp/test_lock"; int main() { int file_desc; struct flock region_to_lock; int res; /* open a file descriptor */ file_desc = open(test_file, O_RDWR | O_CREAT, 0666); if (!file_desc) { fprintf(stderr, "Unable to open %s for read/write\n", test_file); exit(EXIT_FAILURE); } region_to_lock.l_type = F_RDLCK; region_to_lock.l_whence = SEEK_SET; region_to_lock.l_start = 10; region_to_lock.l_len = 5; printf("Process %d, trying F_RDLCK, region %d to %d\n", getpid(), (int)region_to_lock.l_start, (int)(region_to_lock.l_start + region_to_lock.l_len)); res = fcntl(file_desc, F_SETLK, ®ion_to_lock); if (res == -1) { printf("Process %d - failed to lock region\n", getpid()); } else { printf("Process %d - obtained lock region\n", getpid()); } region_to_lock.l_type = F_UNLCK; region_to_lock.l_whence = SEEK_SET; region_to_lock.l_start = 10; region_to_lock.l_len = 5; printf("Process %d, trying F_UNLCK, region %d to %d\n", getpid(), (int)region_to_lock.l_start, (int)(region_to_lock.l_start + region_to_lock.l_len)); res = fcntl(file_desc, F_SETLK, ®ion_to_lock); if (res == -1) { printf("Process %d - failed to unlock region\n", getpid()); } else { printf("Process %d - unlocked region\n", getpid()); } region_to_lock.l_type = F_UNLCK; region_to_lock.l_whence = SEEK_SET; region_to_lock.l_start = 0; region_to_lock.l_len = 50; printf("Process %d, trying F_UNLCK, region %d to %d\n", getpid(), (int)region_to_lock.l_start, (int)(region_to_lock.l_start + region_to_lock.l_len)); res = fcntl(file_desc, F_SETLK, ®ion_to_lock); if (res == -1) { printf("Process %d - failed to unlock region\n", getpid()); } else { printf("Process %d - unlocked region\n", getpid()); } region_to_lock.l_type = F_WRLCK; region_to_lock.l_whence = SEEK_SET; region_to_lock.l_start = 16; region_to_lock.l_len = 5; printf("Process %d, trying F_WRLCK, region %d to %d\n", getpid(), (int)region_to_lock.l_start, (int)(region_to_lock.l_start + region_to_lock.l_len)); res = fcntl(file_desc, F_SETLK, ®ion_to_lock); if (res == -1) { printf("Process %d - failed to lock region\n", getpid()); } else { printf("Process %d - obtained lock on region\n", getpid()); } region_to_lock.l_type = F_RDLCK; region_to_lock.l_whence = SEEK_SET; region_to_lock.l_start = 40; region_to_lock.l_len = 10; printf("Process %d, trying F_RDLCK, region %d to %d\n", getpid(), (int)region_to_lock.l_start, (int)(region_to_lock.l_start + region_to_lock.l_len)); res = fcntl(file_desc, F_SETLK, ®ion_to_lock); if (res == -1) { printf("Process %d - failed to lock region\n", getpid()); } else { printf("Process %d - obtained lock on region\n", getpid()); } region_to_lock.l_type = F_WRLCK; region_to_lock.l_whence = SEEK_SET; region_to_lock.l_start = 16; region_to_lock.l_len = 5; printf("Process %d, trying F_WRLCK with wait, region %d to %d\n", getpid(), (int)region_to_lock.l_start, (int)(region_to_lock.l_start + region_to_lock.l_len)); res = fcntl(file_desc, F_SETLKW, ®ion_to_lock); if (res == -1) { printf("Process %d - failed to lock region\n", getpid()); } else { printf("Process %d - obtained lock on region\n", getpid()); } printf("Process %d ending\n", getpid()); close(file_desc); exit(EXIT_SUCCESS); }