linux下文件锁定有两种:一种是以原子操作方式创建锁文件;另一种是允许锁定文件的一部分,从而独享对这一部分内容的访问。
1、锁文件
许多应用程序只需要能够针对某个资源创建一个锁文件,然后其他程序通过检查这个文件来判断它们是否被允许访问这个资源。创建锁文件使用fcntl.h头文件(楼主机器上位于/usr/include下)定义的open系统调用,并带上o_creat和o_excl标志。这样就以原子操作完成两项工作:确定文件不存在,然后 创建
#include
#include
#include
#include
#include
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);
}
不过在第二次运行以上程序的时候,会提示错误:open failed with error 17(文件已存在,错误码在/usr/include/asm-generic/error-base.h),如果想让程序再次执行成功,就必须删除那个锁文件。在c语言调用中,我们可以使用unlink函数(定义于/usr/include/unistd.h)。另外,以上的代码也不是很合理的,只有open没有close,正常情况下,应该加上以下两行:
(void)close(file_desc);
(void)unlink( "/tmp/lck.test");
关于unlink。unlink原型如下:
#include
int unlink (__const char *__name)
函数功能:删除目录项,并将由__name所引用文件的链接
计数减1,当链接计数为0时,文件被删除。
关于unlink的使用,可以参考《unix环境高级编程》第17章,17.3.2唯一链接,在listen前,先unlink以防文件已经存在,accept后再unlink,防止下次调用处问题。曾经楼主在ace的unix域套接字ace_lsock上遇到address in use,其实就是锁文件已存在没有删除的问题。
2、区域锁定
区域锁定出现,是因为锁文件方式并不适用于访问大型的共享文件。如果一个大文件,由一个程序写入数据,但却由不同的程序同时对这个文件进行更新。处理程序不能等待记录程序结束,所以需要一些协调方法来提供对同一个文件的并发访问。linux提供了2种方法来实现:一是fcntl系统调用和lockf调用。
一个使用fcntl锁定文件的例子如下:
#include
#include
#include
#include
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);
}
程序首先创建一个文件,然后以读写方式打开,添加一些数据。接着在文件中设置2个区域,第一个是0-30,用读(共享)锁;第二个是40-50,用写(独占) 锁,然后调用fcntl来锁定这2个区域。
fcntl参数提供了3个命令选项
f_getlk、f_setlk、f_setlkw,l_type提供的选项有f_rdlck、f_unlck、f_wrlck,分别为读锁,解锁,写锁
测试锁的程序如下:
#include
#include
#include
#include
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);
}
(以上大部分内容来自《linux程序设计》)