Linux记录锁

来源:互联网 发布:软件开发过程文档 编辑:程序博客网 时间:2024/05/16 15:32

记录锁的功能是:当一个进程正在读或者修改文件的某一个部分时,它可以阻止其他进程修改同一文件区。记录锁其实是
字节范围锁,因为它锁定的只是文件中的一个区域,也可能是整个文件。

1.基础介绍

SVR3通过fcntl函数增加了记录锁功能。fcntl函数的原型已经在以前给出,这边再重复一次。

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

对于记录锁,cmd是F_GETLK、F_SETLK或F_SETLKW。第三个参数是一个指向flock结构的指针。

struct flock{      short l_type; //F_RDLCK,F_WRLCK, or F_UNLCK      off_t l_start; //offset in bytes, relative to l_whence      short l_whence; //SEEK_SET, SEEK_CUR or SEEK_END      off_t l_len; //length in bytes; 0 means lock to EOF      pid_t l_pid; //returned with F_GETLK  };  

关于加锁解锁区域注意以下几点:
1.l_start是相对偏移量,l_whence决定了相对偏移量的起点。这与lseek函数中最后两个参数类似。
2.该区域可以在当前文件尾端处开始或越过其尾端处开始,但是不能在文件起始位置之前开始。
3.如若l_len为0,则表示锁的区域从其起点(由l_start和l_whence决定)开始直至最大可能偏移量为止。(也就是不管添加到该文件中多少数据,它们都处于锁的范围内)
4.为了锁整个文件,我们设置l_start和l_whence,使锁的起点在文件起始处,并且说明l_en为0.

上面说到的读写锁和线程互斥中的读写锁原理差不多:

以下说明fcntl函数的三种命令:
F_GETLK:判断由flockptr所描述的锁是否会被另外一把锁所排斥。如果存在一把锁,它阻止创建由flockptr所描述的锁,
则把该现存锁的信息写到flockptr指向的结构中,如果不存在这中情况,则除了将l_type设置为UNLCK之外,flockptr所指向
结构的其他信息保持不变。
F_SETLK:设置由flockptr所描述的锁,如果试图建立一把锁(读锁或者写锁),而按上述兼容性规则不能允许,则fcntl立即
出错返回,此时errno设置为EACCES或EAGAIN。此命令也用来消除由flockptr说明的锁(l_type为UNLOCK)
F_SETLKW:这是F_SETLK的阻塞版本,如果因为当前在所请求区间的某个部分另一个进程已经有一把锁,因而按兼容性
规则由flockptr所请求的锁不能被创建,则使调用进程休眠,如果请求创建的锁已经可用或者休眠由信号终端,则该进程被唤醒。

在设置或释放文件上的锁时,系统按照要求组合或裂开相邻区,例如,若字节100~199是加锁区,需解锁第150字节,则内核
将维持两把锁,一把用于字节100~149,另一把用于字节151~199.
这里写图片描述
假定我们又对第150字节设置锁,那么系统将会把三个相邻的加锁区合并成一个区,其结果如上图的第一图所示。

2.函数调用

2.1请求和释放一把锁

为了避免每次分配flock结构,然后又填入各项信息,可以用lock_reg来处理所有这些细节:

int lock_reg(int fd, int cmd, int type, off_t offset, int whence, 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));}

用以下宏来调用:

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

2.2测试一把锁

pid_t lock_test(int fd, int type, off_t offset, int whence, 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)        err_sys("fcntl error");    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 */}

如果存在一把锁,则函数返回持有该锁的进程的进程ID,否则返回0。
通常用下面的宏来调用函数:

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

注意:进程不能使用lock_test函数测试它自己是否在文件的某一部分持有一把锁。

1 0
原创粉丝点击