文件锁备忘录

多进程读写文件

如果你想进行资源保护的话,完美的资源保护应该满足如下这样的。

1)写与写之间互斥

2)读与写之间互斥

3)读与读之间共享

使用信号量来实现保护的话,只能是一律互斥,包括读与读都是互斥的,使用文件锁可以做到读读共享,读写互斥,写写互斥。

文件锁的读锁与写锁

①读锁和读锁共享:可以重复加读锁,别人加了读锁在没有解锁之前,我依然可以加读锁,这就是共享。

②读锁与写锁互斥:别人加了读锁没有解锁前,加写锁会失败,反过来也是如此。

③写锁与写锁互斥:别人加了写锁在没有解锁前,不能加写锁,加写锁会失败(阻塞或者返回错误)。

文件锁的加锁方式

(1)对整个文件内容加锁

对整个文件加锁是最常用的文件锁的加锁方式。当你对整个文件加锁时,如果文件的长度因为写入新数据或者截短而发生了变化,加锁内容的长度会自动变化,保证对内容变化着的整个文件加锁。

(2)对文件某部分内容加锁

不过一般来说是,对多少内容加锁,就对多少内容解锁,如果你是对整个文件加锁,就将整个文件解锁。但是实际上加锁和实际解锁的长度可以不相同,比如我对1000个字节的内容加了锁,但是可以只对其中的100字节解锁,不过这种情况用的少,知道有这么回事即可。

文件锁的实现

  1. fcntl()
    #include <unistd.h>
    #include <fcntl.h>        
    int fcntl(int fd, int cmd, .../*struct flock *flockptr */ );
    

    fcntl函数有多种功能,我们这里主要介绍实现文件锁的功能,当cmd被设置的是与文件锁相关的宏时,fcntl就是用于实现文件锁。

    fd:文件描述符,指向需要被加锁的文件。

    cmd:实现文件锁时,cmd有三种设置,F_GETLK、F_SETLK和F_SETLKW含义如下:

    F_GETLK:从内核获取文件锁的信息,将其保存到第三个参数,此时第三个参数为struct flock *flockptr。

    F_SETLK:设置第三个参数所代表的文件锁,而且设置的是非阻塞文件锁,也就是如果加锁失败不会阻塞。也就是说加锁失败后如果不想阻塞的话,就是由F_SETLK宏来决定的。

    F_SETLKW:与F_SETLK一样,只不过设置的是阻塞文件锁,也就说加锁不成功的话就阻塞,是由F_SETLKW宏来决定的。

    struct flock
    {
        short l_type;   // Type of lock: F_RDLCK,F_WRLCK, F_UNLCK 
        short l_whence; //How to interpret l_start:SEEK_SET, SEEK_CUR, SEEK_END
        off_t l_start;   // Starting offset for lock 
        off_t l_len;    //Number of bytes to lock 
        pid_t l_pid;    //PID of process blocking our lock(F_GETLK only) 当前正加着锁的那个进程的PID,只有当我们获取一个已存在锁的信息时,才会使用这个成员,这个成员的值不是我们设置的,是由文件锁自己设置的,我们只是获取以查看当前那个进程正加着锁。
    }

    将l_whence和l_start设置为SEEK_SET和0,然后再将l_len设置为0,就表示从文件头加锁到文件末尾,其实就是对整个文件加锁。

     
    flockptr.l_whence=SEEK_SET;                                            
    flockptr.l_start=0;                                            
    flockptr.l_len=0;
    int fd = open(filename, O_RDWR | O_CREAT, LOCKMODE);
    
    int lockfile(int fd, int set)
    {
    struct flock fl;
    
        fl.l_type = F_WRLCK;
        fl.l_start = set;
        fl.l_whence = SEEK_SET;
        fl.l_len = 1;
        return(fcntl(fd, F_SETLK, &fl));
    }
    
    int unlockfile(int fd, int set)
    {
        struct flock fl;
    
        fl.l_type = F_UNLCK;
        fl.l_start = set;
        fl.l_whence = SEEK_SET;
        fl.l_len = 1;
        return(fcntl(fd, F_SETLK, &fl));
    }
    
    pid_t checklock(int fd, int set)
    {
        struct flock fl;
        fl.l_type = F_WRLCK;
        fl.l_start = set;
        fl.l_whence = SEEK_SET;
        fl.l_len = 1;
        if (fcntl(fd, F_GETLK, &fl) == -1)
        {
            return -1;
        }
        if (fl.l_type == F_UNLCK)
        {
            return 0;
        }
        return fl.l_pid;
    }

  2. flock
    #include<sys/file.h>
    int flock(int fd, int operation); //成功返回0,失败返回-1

    fd:指向需要被加锁的文件;

    operation:LOCK_SH:加共享锁; LOCK_EX:加互斥锁; LOCK_UN:解锁 。

    fd = open("./file", O_RDWR|O_CREAT|O_TRUNC|O_APPEND, 0664);

    flock(fd, LOCK_SH);
    write(fd, "hello ", 6);
    write(fd, "world\n", 6);
    flock(fd, LOCK_UN);

    需要注意的是亲缘进程(父子进程),子进程不能使用从父进程继承而来的文件描述符,父子进程flock时必须使用独自open所返回的文件描述符。这一点与fcntl实现的文件锁不一样,父子进程可以使用各自open返回的文件描述符加锁,但是同时子进程也可以使用从父进程继承而来的文件描述符加锁。

    用于多线程时与用于多进程一样,各线程必须使用各自open所返回的文件描述符才能加锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tangcpp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值