linux下文件锁定有两种:一种是以原子操作方式创建锁文件;另一种是允许锁定文件的一部分,从而独享对这一部分内容的访问。
1、锁文件
许多应用程序只需要能够针对某个资源创建一个锁文件,然后其他程序通过检查这个文件来判断它们是否被允许访问这个资源。创建锁文件使用fcntl.h头文件定义的open系统调用,并带上O_CREAT和O_EXCL标志。这样就以原子操作完成两项工作:确定文件不存在,然后创建
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <errno.h>
- int main()
- {
- int file_desc;
- int save_errno;
- file_desc = open("/tmp/LCK.test", O_RDWR | O_CREAT | O_EXCL, 0444);
- if (file_desc == -1) {
- save_errno = errno;
- printf("Open failed with error %d\n", save_errno);
- }
- else {
- printf("Open succeeded\n");
- }
- exit(EXIT_SUCCESS);
- }
- (void)close(file_desc);
- (void)unlink( "/tmp/LCK.test");
- #include <unistd.h>
- int unlink (__const char *__name)
关于unlink的使用,可以参考《unix环境高级编程》第17章,17.3.2唯一链接,在listen前,先unlink以防文件已经存在,accept后再unlink,防止下次调用处问题。曾经楼主在ACE的unix域套接字ACE_LSOCK上遇到address in use,其实就是锁文件已存在没有删除的问题。
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锁定文件的例子如下:
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <fcntl.h>
- const char *test_file = "/tmp/test_lock";
- int main() {
- int file_desc;
- int byte_count;
- char *byte_to_write = "A";
- struct flock region_1;
- struct flock region_2;
- 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);
- }
- /* put some data in the file */
- for(byte_count = 0; byte_count < 100; byte_count++) {
- (void)write(file_desc, byte_to_write, 1);
- }
- /* setup region 1, a shared lock, from bytes 10 -> 30 */
- region_1.l_type = F_RDLCK;
- region_1.l_whence = SEEK_SET;
- region_1.l_start = 10;
- region_1.l_len = 20;
- /* setup region 2, an exclusive lock, from bytes 40 -> 50 */
- region_2.l_type = F_WRLCK;
- region_2.l_whence = SEEK_SET;
- region_2.l_start = 40;
- region_2.l_len = 10;
- /* now lock the file */
- printf("Process %d locking file\n", getpid());
- res = fcntl(file_desc, F_SETLK, ®ion_1);
- if (res == -1) fprintf(stderr, "Failed to lock region 1\n");
- res = fcntl(file_desc, F_SETLK, ®ion_2);
- if (res == -1) fprintf(stderr, "Failed to lock region 2\n");
- /* and wait for a while */
- sleep(60);
- printf("Process %d closing file\n", getpid());
- close(file_desc);
- exit(EXIT_SUCCESS);
- }
fcntl参数提供了3个命令选项
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";
- #define SIZE_TO_TRY 5
- void show_lock_info(struct flock *to_show);
- int main() {
- int file_desc;
- int res;
- struct flock region_to_test;
- int start_byte;
- /* 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", test_file);
- exit(EXIT_FAILURE);
- }
- for (start_byte = 0; start_byte < 99; start_byte += SIZE_TO_TRY) {
- /* set up the region we wish to test */
- region_to_test.l_type = F_WRLCK;
- region_to_test.l_whence = SEEK_SET;
- region_to_test.l_start = start_byte;
- region_to_test.l_len = SIZE_TO_TRY;
- region_to_test.l_pid = -1;
- printf("Testing F_WRLCK on region from %d to %d\n",
- start_byte, start_byte + SIZE_TO_TRY);
- /* now test the lock on the file */
- res = fcntl(file_desc, F_GETLK, ®ion_to_test);
- if (res == -1) {
- fprintf(stderr, "F_GETLK failed\n");
- exit(EXIT_FAILURE);
- }
- if (region_to_test.l_pid != -1) {
- printf("Lock would fail. F_GETLK returned:\n");
- show_lock_info(®ion_to_test);
- }
- else {
- printf("F_WRLCK - Lock would succeed\n");
- }
- /* now repeat the test with a shared (read) lock */
- /* set up the region we wish to test */
- region_to_test.l_type = F_RDLCK;
- region_to_test.l_whence = SEEK_SET;
- region_to_test.l_start = start_byte;
- region_to_test.l_len = SIZE_TO_TRY;
- region_to_test.l_pid = -1;
- printf("Testing F_RDLCK on region from %d to %d\n",
- start_byte, start_byte + SIZE_TO_TRY);
- /* now test the lock on the file */
- res = fcntl(file_desc, F_GETLK, ®ion_to_test);
- if (res == -1) {
- fprintf(stderr, "F_GETLK failed\n");
- exit(EXIT_FAILURE);
- }
- if (region_to_test.l_pid != -1) {
- printf("Lock would fail. F_GETLK returned:\n");
- show_lock_info(®ion_to_test);
- }
- else {
- printf("F_RDLCK - Lock would succeed\n");
- }
- } /* for */
- close(file_desc);
- exit(EXIT_SUCCESS);
- }
- void show_lock_info(struct flock *to_show) {
- printf("\tl_type %d, ", to_show->l_type);
- printf("l_whence %d, ", to_show->l_whence);
- printf("l_start %d, ", (int)to_show->l_start);
- printf("l_len %d, ", (int)to_show->l_len);
- printf("l_pid %d\n", to_show->l_pid);
- }
文件锁的竞争
#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);
}