Chinaunix首页 | 论坛 | 博客
  • 博客访问: 682847
  • 博文数量: 516
  • 博客积分: 4119
  • 博客等级: 上校
  • 技术积分: 4288
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-30 17:29
文章分类

全部博文(516)

文章存档

2014年(4)

2013年(160)

2012年(352)

分类:

2012-11-01 11:23:07

原文地址:文件锁定 作者:graylocus

程序需要共享数据,这通常是通过文件来实现的,因此,建立某种控制文件的方式就非常重要了。当一个程序正在对文件进行写操作时,文件就会进入一个暂时状态,在这个状态下,如果另外一个程序尝试读这个文件,就会停下来等待这个状态的结束。
linux下文件锁定有两种:一种是以原子操作方式创建锁文件;另一种是允许锁定文件的一部分,从而独享对这一部分内容的访问。

    1、锁文件

    许多应用程序只需要能够针对某个资源创建一个锁文件,然后其他程序通过检查这个文件来判断它们是否被允许访问这个资源。创建锁文件使用fcntl.h头文件(楼主机器上位于/usr/include下)定义的open系统调用,并带上O_CREAT和O_EXCL标志。这样就以原子操作完成两项工作:确定文件不存在,然后创建  

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5. #include <errno.h>
  6.   
  7. int main()
  8. {
  9.     int file_desc;
  10.     int save_errno;
  11.   
  12.     file_desc = open("/tmp/LCK.test", O_RDWR | O_CREAT | O_EXCL, 0444);
  13.     if (file_desc == -1) {
  14.         save_errno = errno;
  15.         printf("Open failed with error %d\n", save_errno);
  16.     }
  17.     else {
  18.         printf("Open succeeded\n");
  19.     }
  20.     exit(EXIT_SUCCESS);
  21. }


 不过在第二次运行以上程序的时候,会提示错误: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 <unistd.h>  

int unlink (__const char *__name)  

函数功能:删除目录项,并将由__name所引用文件的链接计数减1,当链接计数为0时,文件被删除。

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5. #include <errno.h>

  6. const char *lock_file="/tmp/LCK.test2";

  7. int main()
  8. {
  9.     int file_desc;
  10.     int tries=10;
  11.     while(tries--){
  12.     file_desc = open(lock_file,O_RDWR | O_CREAT | O_EXCL, 0444);
  13.     if (file_desc == -1) {
  14.         printf("%d - lock already presetn\n", getpid());
  15.         sleep(3);
  16.     }
  17.     else {
  18.         printf("%d - I have exclusive access\n",getpid());
  19.         sleep(1);
  20.         (void)close(file_desc);
  21.         (void)unlink(lock_file);
  22.         sleep(2);
  23.         }
  24.     }
  25.     exit(EXIT_SUCCESS);
  26. }


    关于unlink的使用,可以参考《unix环境高级编程》第17章,17.3.2唯一链接,在listen前,先unlink以防文件已经存在,accept后再unlink,防止下次调用处问题。曾经楼主在ACE的unix域套接字ACE_LSOCK上遇到address in use,其实就是锁文件已存在没有删除的问题。

     2、区域锁定

    区域锁定出现,是因为锁文件方式并不适用于访问大型的共享文件。如果一个大文件,由一个程序写入数据,但却由不同的程序同时对这个文件进行更新。处理程序不能等待记录程序结束,所以需要一些协调方法来提供对同一个文件的并发访问。linux提供了2种方法来实现:一是fcntl系统调用和lockf调用。fcntl是最常用的接口,lockf和fcntl非常相似,它一般作为fcntl的备选接口,但是,fcntl和lockf的锁定机制不能同时工作,这是因为,它们使用不同的底层实现,因此绝不要混合使用这两种类型的调用,应该坚持使用其中的一种。

#include

int fcntl(int fildes,int command,...);

fcntl对一个打开的文件描述符进行操作,并能根据command参数的设置完成不同的任务。有三个用于文件锁定的命令选项:

*F_GETLK:用于获取fildes打开的文件的锁信息,不会尝试去锁文件。

*F_SETLK:这个命令对fildes指定的文件的某个区域枷锁或解锁。

*F_SETLKW:与F_SETLK命令作用相同,但在无法获取锁时,这个调用将等待直到可以为止,一旦这个调用开始等待,只有在可以获取锁或收到一个信号时才会返回。

command是要和flock结构配合使用的。

程序对某个文件拥有的所有锁都将在相应的文件描述符被关闭时自动清除,在程序结束时也会自动清除各种锁。

当使用这三个选项时,第三个参数必须是一个指向flock结构的指针,实际的函数原型:

int fcntl(iint fildes,int command,struct flock *flock_structure);

3.锁定状态下的读写操作

当对文件区域枷锁之后,必须使用底层的read和write调用来访问文件中的数据,而不要使用更高级的fread和fwrite,这是因为fread和fwrite会对读写的数据进行缓存,所以执行一次fread调用来读取文件中的头100个字节可能会读取超过100个字节的数据,并将多余的数据在函数库中进行缓存。如果程序再次使用fread来读取下100个字符的数据,它实际上将读取已缓冲在函数库中的数据,而不会引发一个底层的read调用来从文件中读取更多的数据。

一个使用fcntl锁定文件的例子如下:

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5.   
  6. const char *test_file = "/tmp/test_lock";
  7.   
  8. int main() {
  9.     int file_desc;
  10.     int byte_count;
  11.     char *byte_to_write = "A";
  12.     struct flock region_1;
  13.     struct flock region_2;
  14.     int res;
  15.   
  16.         /* open a file descriptor */
  17.     file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
  18.     if (!file_desc) {
  19.         fprintf(stderr, "Unable to open %s for read/write\n", test_file);
  20.         exit(EXIT_FAILURE);
  21.     }
  22.   
  23.         /* put some data in the file */
  24.     for(byte_count = 0; byte_count < 100; byte_count++) {
  25.         (void)write(file_desc, byte_to_write, 1);
  26.     }
  27.   
  28.         /* setup region 1, a shared lock, from bytes 10 -> 30 */
  29.     region_1.l_type = F_RDLCK;
  30.     region_1.l_whence = SEEK_SET;
  31.     region_1.l_start = 10;
  32.     region_1.l_len = 20;
  33.       
  34.         /* setup region 2, an exclusive lock, from bytes 40 -> 50 */
  35.     region_2.l_type = F_WRLCK;
  36.     region_2.l_whence = SEEK_SET;
  37.     region_2.l_start = 40;
  38.     region_2.l_len = 10;
  39.   
  40.         /* now lock the file */
  41.     printf("Process %d locking file\n", getpid());
  42.     res = fcntl(file_desc, F_SETLK, ion_1);
  43.     if (res == -1) fprintf(stderr, "Failed to lock region 1\n");
  44.     res = fcntl(file_desc, F_SETLK, ion_2);
  45.     if (res == -1) fprintf(stderr, "Failed to lock region 2\n");
  46.   
  47.         /* and wait for a while */
  48.     sleep(60);
  49.   
  50.     printf("Process %d closing file\n", getpid());
  51.     close(file_desc);
  52.     exit(EXIT_SUCCESS);
  53. }

程序首先创建一个文件,然后以读写方式打开,添加一些数据。接着在文件中设置2个区域,第一个是0-30,用读(共享)锁;第二个是40-50,用写(独占) 锁,然后调用fcntl来锁定这2个区域。
       fcntl参数提供了3个命令选项

       F_GETLK、F_SETLK、F_SETLKW,l_type提供的选项有F_RDLCK、F_UNLCK、F_WRLCK,分别为读锁,解锁,写锁

       测试锁的程序如下:


点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5.   
  6.   
  7. const char *test_file = "/tmp/test_lock";
  8. #define SIZE_TO_TRY 5
  9.   
  10. void show_lock_info(struct flock *to_show);
  11.   
  12.   
  13. int main() {
  14.     int file_desc;
  15.     int res;
  16.     struct flock region_to_test;
  17.     int start_byte;
  18.       
  19.         /* open a file descriptor */
  20.     file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
  21.     if (!file_desc) {
  22.         fprintf(stderr, "Unable to open %s for read/write", test_file);
  23.         exit(EXIT_FAILURE);
  24.     }
  25.   
  26.     for (start_byte = 0; start_byte < 99; start_byte += SIZE_TO_TRY) {
  27.             /* set up the region we wish to test */
  28.         region_to_test.l_type = F_WRLCK;
  29.         region_to_test.l_whence = SEEK_SET;
  30.         region_to_test.l_start = start_byte;
  31.         region_to_test.l_len = SIZE_TO_TRY;
  32.         region_to_test.l_pid = -1;
  33.   
  34.         printf("Testing F_WRLCK on region from %d to %d\n",
  35.                start_byte, start_byte + SIZE_TO_TRY);
  36.           
  37.             /* now test the lock on the file */
  38.         res = fcntl(file_desc, F_GETLK, ion_to_test);
  39.         if (res == -1) {
  40.             fprintf(stderr, "F_GETLK failed\n");
  41.             exit(EXIT_FAILURE);
  42.         }
  43.         if (region_to_test.l_pid != -1) {
  44.             printf("Lock would fail. F_GETLK returned:\n");
  45.             show_lock_info(ion_to_test);
  46.         }
  47.         else {
  48.             printf("F_WRLCK - Lock would succeed\n");
  49.         }
  50.   
  51.             /* now repeat the test with a shared (read) lock */
  52.           
  53.             /* set up the region we wish to test */
  54.         region_to_test.l_type = F_RDLCK;
  55.         region_to_test.l_whence = SEEK_SET;
  56.         region_to_test.l_start = start_byte;
  57.         region_to_test.l_len = SIZE_TO_TRY;
  58.         region_to_test.l_pid = -1;
  59.   
  60.         printf("Testing F_RDLCK on region from %d to %d\n",
  61.                start_byte, start_byte + SIZE_TO_TRY);
  62.           
  63.             /* now test the lock on the file */
  64.         res = fcntl(file_desc, F_GETLK, ion_to_test);
  65.         if (res == -1) {
  66.             fprintf(stderr, "F_GETLK failed\n");
  67.             exit(EXIT_FAILURE);
  68.         }
  69.         if (region_to_test.l_pid != -1) {
  70.             printf("Lock would fail. F_GETLK returned:\n");
  71.             show_lock_info(ion_to_test);
  72.         }
  73.         else {
  74.             printf("F_RDLCK - Lock would succeed\n");
  75.         }
  76.   
  77.     } /* for */
  78.       
  79.     close(file_desc);
  80.     exit(EXIT_SUCCESS);
  81. }
  82.   
  83. void show_lock_info(struct flock *to_show) {
  84.     printf("\tl_type %d, ", to_show->l_type);
  85.     printf("l_whence %d, ", to_show->l_whence);
  86.     printf("l_start %d, ", (int)to_show->l_start);
  87.     printf("l_len %d, ", (int)to_show->l_len);
  88.     printf("l_pid %d\n", to_show->l_pid);
  89. }

阅读(308) | 评论(0) | 转发(0) |
0

上一篇:内存管理

下一篇:C/C++野指针

给主人留下些什么吧!~~