LINUX文件锁定

 linux下文件锁定有两种:一种是以原子操作方式创建锁文件;另一种是允许锁定文件的一部分,从而独享对这一部分内容的访问。

    1、锁文件

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

[cpp]  view plain copy
  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,正常情况下,应该加上以下两行:

[cpp]  view plain copy
  1. (void)close(file_desc);  
  2. (void)unlink( "/tmp/LCK.test");  
     关于unlink,可以参考 http://bbs.chinaunix.net/thread-1920145-1-1.html  的说法。unlink原型如下:

[html]  view plain copy
  1. #include <unistd.h>  
  2. 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<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锁定文件的例子如下:

[cpp]  view plain copy
  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,分别为读锁,解锁,写锁

       测试锁的程序如下:

[cpp]  view plain copy
  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. }  


文件锁的竞争

#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);
}


其他锁的命令
#include<unistd.h>
int lockf(int fildes, int function, off_t size_to_lock);
funciton参数的取值如下所示:
F_ULOCK:解锁
F_LOCK:设置独占锁
F_TLOCK:测试并设置独占锁
F_TEST:测试其他进程设置的锁
size_to_lock参数是操作的字节数,它从文件的当前偏移值开始计算。

你可能感兴趣的:(linux,command,File,byte,Descriptor,structure)