文件锁与NFS文件锁

原文:http://www.cnblogs.com/zhenjing/archive/2011/07/04/filelock.html

缘起

因项目需要,自行设计一套通用的文件读写锁,要求该机制能用于本地文件系统和NFS文件系统。

内核的文件数据结构

内核中有3个数据结构和文件直接相关,分别是:file descriptor table, file table and i-node table。其中file descriptor table是进程私有的;file table和inode table是独立于进程的。进程内部的文件描述符fd只在该进程有才有意义。File table可在进程内共享,也可在进程间共享。I-node table则在整个系统中共享。

文件描述符fd可由Open,dup和fork等操作创建;file table只能有open创建;i-node table在创建真实文件是创建。

文件锁与NFS文件锁_第1张图片

摘自Apue-3.12

 

摘自Apue-14.3

 

摘自TLPI-5.4

对TLPI-5.4的图作如下说明:

1) A中的fd 1和20共享file table记录,可认为fd 20复制于fd 1(可也反过来)

2) A和B的fd 2共享file table记录,可认为B由A fork产生,B重定向fd 0和1。

3) File table中的0和86指向相同的文件,这意味着0和86由open相同的文件。

理解这个3个数据结构的关系大体可知道linux是如何管理文件。Stat和fcntl可获取或者修改相关的文件信息,结合这两个接口将有助于理解这3个文件结构。

文件记录锁fcntl

文件记录锁位于i-node table这个结构中,使用PID作为锁拥有者的标识。这使其拥有如下特点:

1) 记录锁采用(PID, start,end)三元组作为锁标识,一个文件可拥有多个记录锁,同一区域只允许有一个记录锁。

2) 当进程终止(正常/不正常),该进程拥有的所有记录锁都将释放。

3) 同一个进程中,指向同一文件(i-node)的fd都可以操作该文件上的记录锁:如释放、修改等。显式调用F_UNLCK和close(fd)都将释放锁,close将释放整个文件中该进程拥有的所有记录锁。

4) 记录锁不被fork的子进程继承(PID不同)。

5) 记录锁的类型转换、改变锁范围等操作均为原子的。

6) 未设置FD_CLOEXEC时,记录锁将被exec后的进程继承(PID相同)。

7) 记录锁对文件打开mode有要求:加读锁要求fd有读权限;加写锁要求fd有写权限。

fcntl的使用示例和测试代码:

?
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
 
#include <fcntl.h>
 
int     lock_reg(int,int,int, off_t, int, off_t);
#define read_lock(fd, offset, whence, len) \
             lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define readw_lock(fd, offset, whence, len) \
             lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len) \
             lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len) \
             lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len) \
             lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))
 
pid_t   lock_test( int , int , off_t, int , off_t);
 
#define is_read_lockable(fd, offset, whence, len) \
             (lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len) \
             (lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)
 
 
int lock_reg(intfd,intcmd,inttype, off_t offset, intwhence, off_t len)
{
     struct flock    lock;
 
     lock.l_type = type;     /* F_RDLCK, F_WRLCK, F_UNLCK */
     lock.l_start = offset;  /* byte offset, relative to l_whence */
     lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
     lock.l_len = len;       /* #bytes (0 means to EOF) */
 
     return (fcntl(fd, cmd, &lock));
}
 
/* Note: lock_test always success with the obtained lock process */
pid_t lock_test( int fd,inttype, off_t offset, intwhence, off_t len)
{
     struct flock    lock;
 
     lock.l_type = type;     /* F_RDLCK or F_WRLCK */
     lock.l_start = offset;  /* byte offset, relative to l_whence */
     lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
     lock.l_len = len;       /* #bytes (0 means to EOF) */
 
     if (fcntl(fd, F_GETLK, &lock) < 0)
         perror ( "fcntl error" );
 
     /* printf("F_RDLCK=%d, F_WRLCK=%d, F_UNLCK=%d\n", F_RDLCK, F_WRLCK, F_UNLCK); */
     printf ( "    l_type=%d, l_start=%lu, l_where=%d, l_len=%lu, l_pid=%d\n"
             , lock.l_type, lock.l_start, lock.l_whence, lock.l_len, lock.l_type);
 
     if (lock.l_type == F_UNLCK)
         return (0);      /* false, region isn't locked by another proc */
     return (lock.l_pid); /* true, return pid of lock owner */
}
 
/* ========================================================  */
 
void Usage(constchar* program)
{
     fprintf (stderr, "Usage: %s read/read2/write/rd-wr/wr-rd/fork/dup file time\n" , program);
     exit (-1);
}
     
int main(intargc,char*argv[])
{
     if (argc < 3){
         Usage(argv[0]);
     }
     int interval = 10;
     if (argc > 3) {
         interval = atoi (argv[3]);
     }
     printf ( "PID=%d\n" , getpid());
 
     char * path = argv[2];
     if ( strcmp (argv[1], "read" ) == 0 ) {
         int fd = open(path, O_RDONLY);
         if (fd == -1) {
             perror ( "open" );
             exit (0);
         }
         printf ( "read lock try\n" );
         /*  read lock entire file */
         if ( read_lock(fd, 0, SEEK_SET, 0) < 0){
             perror ( "read_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "read lock success.\n" );
         printf ( "Read Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
         
         sleep(interval);
          
         exit (0);
     }
     else if(strcmp(argv[1],"read2") == 0 ){
         int fd = open(path, O_RDONLY);
         if (fd == -1) {
             perror ( "open" );
             exit (0);
         }
         printf ( "read lock try\n" );
         /*  read lock entire file */
         if ( read_lock(fd, 0, SEEK_SET, 0) < 0){
             perror ( "read_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "read lock success.\n" );
         printf ( "Read Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
 
         if ( read_lock(fd, 0, SEEK_SET, 1) < 0){
             perror ( "read_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "read lock 2 success.\n" );
         sleep(interval);
          
         exit (0);
     }
     else if(strcmp(argv[1],"write") == 0) {
         int fd = open(path, O_WRONLY );
         printf ( "file: %s, fd=%d\n" , path, fd);
         if (fd == -1) {
             perror ( "open" );
             exit (0);
         }
         printf ( "write lock try\n" );
         /*  write lock entire file */
         if ( write_lock(fd, 0, SEEK_SET, 0) < 0){
             perror ( "write_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             printf ( "Read Lock by pid=%d\n" , lock_test(fd, F_RDLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "write lock success.\n" );
         printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
          
         int ret = write(fd, path, strlen(path) );
         if (ret != strlen (path)){
             perror ( "write" );
         }
         sleep(interval);
          
         close(fd);
         exit (0);
     }
     else if(strcmp(argv[1],"rd-wr") == 0){
         int fd = open(path, O_RDWR);   /* O_RDWR  */
         if (fd == -1) {
             perror ( "open" );
             exit (0);
         }
         /*  read lock entire file */
         if ( read_lock(fd, 0, SEEK_SET, 0) < 0){
             perror ( "read_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "read lock success.\n" );
         printf ( "Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
   
         /* if( un_lock(fd, 0, SEEK_SET, 0) < 0){
             perror("un-lock:");
         }                                        */
 
         if ( write_lock(fd, 0, SEEK_SET, 1) < 0){
             perror ( "write_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "write lock success.\n" );
         printf ( "Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
         sleep(interval);
          
         exit (0);
    
     }
     else if(strcmp(argv[1],"wr-rd") == 0){
         int fd = open(path, O_RDWR);   /* O_RDWR */
         if (fd == -1) {
             perror ( "open" );
             exit (0);
         }
         /*  write lock entire file */
         if ( write_lock(fd, 0, SEEK_SET, 0) < 0){
             perror ( "write_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "write lock success.\n" );
         printf ( "Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
          
         /* if( un_lock(fd, 0, SEEK_SET, 0) < 0){
             perror("un-lock:");
         }                                        */
 
         if ( read_lock(fd, 0, SEEK_SET, 0) < 0){
             perror ( "read_lock: " );
             printf ( "Write Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
             exit (0);
         }
         printf ( "read lock success.\n" );
         printf ( "Lock by pid=%d\n" , lock_test(fd, F_WRLCK, 0, SEEK_SET, 0 ));
 
         sleep(interval);
          
         exit (0);
     }
     else if(strcmp(argv[1],"fork") == 0){
         int fd = open(path, O_RDWR);  

你可能感兴趣的:(数据结构,server,File,table,Path,Descriptor)