文件锁机制

| 分类 分布式文件系统  cephfs  文件锁  | 标签 文件系统 

需要基础知识介绍:

linux支持的文件锁技术主要包括以下2种类型的锁
(1)劝告锁(advisory lock)
内核只提供加锁以及检测文件是否已经加锁的手段,但是内核并不参与锁的控制和协调。也就是说,
如果有进程不遵守“游戏规则”,不检查目标文件是否已经由别的进程加了锁就往其中写入数据,那么
内核是不会加以阻拦的。因此,劝告锁并不能阻止进程对文件的访问,而只能依靠各个进程在访问文
件之前检查该文件是否已经被其他进程加锁来实现并发控制。
(2)强制锁(mandatory lock)
与劝告锁不同,强制锁是一种内核强制采用的文件锁,它是从 System V Release 3 开始引入的。
每当有系统调用 open()、read() 以及write() 发生的时候,内核都要检查并确保这些系统调用
不会违反在所访问文件上加的强制锁约束。也就是说,如果有进程不遵守游戏规则,硬要往加了锁
的文件中写入内容,内核就会加以阻拦:
a> 如果一个文件已经被加上了读锁或者共享锁,那么其他进程再对这个文件进行写操作就会被内核阻止;
b> 被加上了写锁或者排他锁,那么其他进程再对这个文件进行读取或者写操作就会被内核阻止。
c> 访问一个已经加有强制锁的文件,只有在读锁可以正常运行。写锁时,将被阻塞

文件锁类型:

Linux调用文件锁,有以下2种类型,lockf是库函数,是fcntl的封装版本,不单独介绍. (1)flock (2)fcntl

flock:

flock的特性包括以下几个方面:
(1)只能对整个文件进行加锁
(2)flock只能产生劝告性锁
(3)flock可以有共享锁和排它锁 (4)flock和fcntl/lockf的区别主要在fork和dup时候的区别 (5)flock不能在NFS文件系统上使用,如果要在NFS使用文件锁,请使用fcntl。

函数原型:
int flock(int fd,int operation);
operation包括如下4种类型
(1)LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定
(2)LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定
(3)LOCK_UN 解除文件锁定状态
(4)LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做OR(|)组合。

示例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/file.h>

int main()
{
    int fd,i;
    char path[]="/home/taoyong/test.txt";
    char s[]="writing.../nwriting....../n";
    extern int errno;
    fd=open(path,O_WRONLY|O_CREAT|O_APPEND);
    if(fd!=-1)
            {
        printf("open file %s ./n",path);

            if(flock(fd,LOCK_EX|LOCK_NB)==0)
            {
            printf("the file was locked by the process./n");    
                if(-1!=write(fd,s,sizeof(s)))
                    {
                printf("write %s to the file %s/n",s,path);
                        }
                else
                       {
                printf("cannot write the file %s/n",path);
                printf("errno:%d/n",errno);
                printf("errMsg:%s/n",strerror(errno));
                    }        
                
            }
        else
            {
            printf("the file was locked by other process.Can't write.../n");
                printf("errno:%d:",errno);
            }
        
        close(fd);


            }
        else
           {
        printf("cannot open file %s/n",path);
        printf("errno:%d/n",errno);
        printf("errMsg:%s",strerror(errno));
            }
}

fcntl:

fcntl括以下几个方面:
(1)可以对文件某一部分加锁
(2) 加读锁(共享锁)文件必须是读打开的,加写锁(排他锁)文件必须是写打开。
(3)进程不能使用F_GETLK命令来测试它自己是否再文件的某一部分持有一把锁。
(4)进程终止时,他所建立的所有文件锁都会被释放(同flock)。
(5)任何时候关闭一个描述符时,则该进程通过这一描述符可以引用的文件上的任何一把锁都被释放(这些锁都是该进程设置的),这一点与flock不同。
(6) fork产生的子进程不继承父进程所设置的锁,这点与flock也不同。(因为flock创建的锁是和文件打开表项(struct file)
相关联的,而不是fd,所以复制出了fd都可以操作这把锁,所以子进程继承了父进程的锁。flock里面要关闭所有复制出的fd,锁才会释放)
(7)在执行exec后,新程序可以继承原程序的锁,这点和flock是相同的。(如果对fd设置了close-on-exec,则exec前会关闭fd,相应文件的锁也会被释放)
(8)支持强制性锁(跟flock不同)。

函数原型:int fcntl(int fd, int cmd, struct flock * lock)
其中第3个参数arg由cmd来控制
cmd的功能包括如下部分:
(1)复制一个现有的描述符(cmd=F_DUPFD/F_DUPFD_CLOEXEC) (2)获得/设置文件描述符标记(cmd=F_GETFD/F_SETFD) (3)获得/设置文件状态标记(cmd=F_GETFL/F_SETFL) (4)获得/设置异步I/O所有权(cmd=F_GETOWN/F_SETOWN) (5)获得/设置记录锁(cmd=F_GETLK/F_SETLK/F_SETLKW)

代码示例:

> #include <unistd.h>   
#include <fcntl.h>   
#include <stdio.h>   
#include <stdlib.h>   
#include <errno.h>    
#include <string.h>   
#define ERR_EXIT(m) \    
    do { \   
        perror(m); \   
        exit(EXIT_FAILURE); \    
    } while(0)    
int main(int argc, char *argv[])   
{   
    int fd;    
    fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, 0664);    
    if (fd == -1)    
        ERR_EXIT("open error");     
    /* 只有对文件有相应的读写权限才能施加对应的文件锁 */    
    struct flock lock;    
    memset(&lock, 0, sizeof(lock));    
    lock.l_type = F_WRLCK; // 排他锁,即不允许其他进程再对其加任何类型的锁,但读锁(共享锁)允许    
    lock.l_whence = SEEK_SET;    
    lock.l_start = 0; //从文件开头开始锁定    
    lock.l_len = 0; // 文件全部内容锁住    
    if (fcntl(fd, F_SETLK, &lock) == 0)    
    {   
        /* 若为F_SETLKW,这时如果锁已经被其他进程占用,则此进程会阻塞直到其他进程释放锁*/   
        printf("lock success\n");   
        printf("press any key to unlock\n");   
        getchar();   
        lock.l_type = F_UNLCK;   
        if (fcntl(fd, F_SETLK, &lock) == 0)    
            printf("unlock success\n");   
        else   
            ERR_EXIT("unlock fail");   
    }   
    else   
        ERR_EXIT("lock fail");   
    return 0; //进程退出会对所有文件解锁    
}  

参考:
https://www.cnblogs.com/charlesblc/p/6287631.html
https://www.ibm.com/developerworks/cn/linux/l-cn-filelock/index.html


上一篇     下一篇