41-fcntl设置文件锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35733751/article/details/82925807

1. 文件锁

当多个进程打开同一文件进行读写时可能会出现数据混乱的问题,原因在于进程间共享同一文件读写指针位置,也就是f_pos(关于f_pos参考:4-文件描述符与打开的文件之间的关系的第三小节file结构体)。

解决这个问题的方法有很多,比如可以使用信号量完成进程同步,但通常使用文件锁会更好一些,因为内核会将锁跟文件关联起来,这就需要借助fcntl函数来实现对一个文件进行加锁。只有拿到锁的进程可以对文件进行读写操作,而没有获得锁的进程操作文件可以打开文件,但无法执行read、write操作,以此来防止进程间对同一文件进行读写出现的数据混乱问题。

 

2. fcntl函数

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

参数fd:指定文件描述符

 

参数cmd:一般用于设置文件锁,F_SETLK和F_SETLKW 用于加锁解锁, F_GETLK用于获取文件锁。

F_SETLK设置文件锁,如果另一进程已经加锁,那么fcntl将会失败并返回EAGAIN错误,不会阻塞。

F_SETLKW设置文件锁,如果另一进程已经加锁,那么该进程将会阻塞,直到文件锁被释放。

F_GETLK实际上是用于获取文件锁的相关信息,以检测能否设置F_SETLK或F_SETLKW进行加锁。

 

参数flock具体定义:

struct flock {
    ...
    short l_type;    	//锁的类型:F_RDLCK(读方式加锁) 、F_WRLCK(写方式加锁) 、F_UNLCK(解锁)
    short l_whence;  	//参考偏移位置:SEEK_SET、SEEK_CUR、SEEK_END 
    off_t l_start;   	//具体起始偏移位置,0表示文件开头
    off_t l_len;     	//长度:其实是表示加锁的范围(加多少个字节),如果为0表示整个文件加锁
    pid_t l_pid;     	//持有该锁的进程ID:(只有调用F_GETLK only,才会用到这个参数)
    ...
};

 

加锁范围:

1. 如果 len > 0, [whence+start  ,  whence+start+len) ,包左不包右

2. 如果 len = 0, [whence+start  , ∞),包左不包右,表示对整个文件加锁

以whence为参考位置开始,加上start就是具体的起始位置。例如:whence = SEEK_SET,start = 10,那么参考位置就是文件开头(偏移为0的位置),加上start后,具体的起始位置就是偏移为10的位置。

 

3. 文件锁示例

多个进程对加锁文件进行访问:

1. 首先测试两个进程间以读方式加锁

2. 然后测试两个进程间以写方式加锁

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

void sys_err(char *str){
    perror(str);
    exit(1);
}

int main(int argc, char *argv[]) {
    int fd;
    struct flock f_lock;
    //参数不够
    if (argc < 2) {
        printf("./a.out filename\n");
        exit(1);
    }
    //打开文件失败
    if ((fd = open(argv[1] , O_RDWR)) < 0){
		  sys_err("open");
	}
    //选用写琐
    f_lock.l_type = F_WRLCK;  
    //选用读琐      
    //f_lock.l_type = F_RDLCK;          
	
    //设置文件指针为文件开头
    f_lock.l_whence = SEEK_SET;
    //起始偏移,0表示文件开头
    f_lock.l_start = 0;
    // l_len表示加锁的长度,0表示整个文件加锁 
    f_lock.l_len = 0; 
             
    //设置属性,让线程阻塞(F_SETLKW),直到加锁成功
    fcntl(fd, F_SETLKW, &f_lock);
    //如果加锁成功,打印get flock
    printf("get flock\n");

    //为了测试效果明显,休眠10秒
    sleep(10);

    //设置锁类型为解锁
    f_lock.l_type = F_UNLCK;
    //设置属性,解锁不成功阻塞
    fcntl(fd, F_SETLKW, &f_lock);
    //解锁成功打印unflock
    printf("un flock\n");
    close(fd);
    return 0;
}

 

测试两个进程间以写方式加锁,程序执行结果:

从图中可以看出,以写方式进行加锁的话,那么左1进程对文件以写方式加锁成功,右1进程对文件以写方式加锁会失败并阻塞,等到左1进程释放写锁后,右1进程立马获取到了写锁。

 

 

修改部分代码,测试两个进程间以读方式加锁:

//选用写琐
//f_lock.l_type = F_WRLCK;  
//选用读琐      
f_lock.l_type = F_RDLCK;       

以读方式进行加锁的话,那么左1进程对文件以读方式加锁成功,右1进程对文件以读方式加锁也会成功。

由此我们可以得出结论:进程间对文件进行加文件锁也遵循“读共享、写独占”特性。但!如若进程不加锁直接操作文件,依然可访问成功,但数据势必会出现混乱。

 

 

猜你喜欢

转载自blog.csdn.net/qq_35733751/article/details/82925807