POSIX记录锁

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

假设一种情况,如果有两个进程同时操作同一个文件,那么最后这个文件的状态会是如何?

在Linux系统中,是允许多个进程同时操作一个文件的,而文件的状态是由最后一个修改进程决定的。但是如果想要保证当前进程的修改是排他的,比如操作一个数据库文件,该怎么办呢?

这时就可以使用记录锁功能保证对文件的操作是排他的,独占的。类UNIX系统中,存在很多种记录锁的实现,本文主要介绍POSIX.1标准的记录锁,POSIX全称为“可移植性的UNIX操作系统接口标准”,该标准是为了方便移植而定义的统一接口,在各种类UNIX系统中都能够兼容,所以重点介绍它。

POSIX.1定义的记录锁接口如下:

#include <fcntl.h>

int fcntl (int fd, int cmd, ...);

实际上是基于fcntl函数实现的,该函数可以根据cmd值的不同,而选择是否接收第三个参数。对于记录锁来说:
需要第三参数:

struct flock {
	short l_type;
	short l_whence;  //SEEK_SET,SEEK_CUR,SEEK_END
	off_t l_start;
	off_t l_len;   //以字节为单位的加锁范围长度,0表示全局到EOF范围
	pid_t l_pid;   //加锁的进程id,F_GETLK时会返回
};

l_type表示操作类型,可以接受读锁(F_RDLOCK),写锁(F_WRLOCK),解锁(F_UNLOCK)三种值,记录锁是支持文件区域加锁的,l_whence和l_start搭配使用表示该文件锁的范围起始位置,l_len表示加锁长度,l_pid为加锁的进程id。
特别值得注意的是,如果l_whence配置为SEEK_SET,l_start配置为0,l_len也配置为0的话,并不是代表此记录锁加锁区域为0,而是表示对整个文件全局范围加锁,后续新写入的区域也被加锁。
第二个参数cmd:

F_GETLK:判断是否能够获取到锁,如果判断能够获取锁成功,则返回结构体中l_type会被设置为F_UNLOCK,否则返回当前已经持锁的结构体。
F_SETLK:非阻塞获取锁,如果成功则返回0,否则errno返回EACCESS或者EAGAIN。锁的类型由第三个参数传入,可以是读锁也可以是写锁,当然也能用来解锁UNLOCK。
F_SETLKW:阻塞获取锁,功能和SETLK一样,但是是阻塞获取。阻塞状态可以被信号中断。

完整加锁示例:

int f_lock(int fd)
{
	int ret = 0;
	struct flock fl;

	fl.l_type = F_WRLOCK;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 0;
	ret = fcntl(fd, F_SETLK, &fl);

	return ret;
}

记录锁的隐含释放规则

1.当获取记录锁的进程终止时,记录锁是自动释放的;
2.当同一个文件对应的多个fd中有任一个fd关闭时,该文件对应的记录锁是自动释放的;
3.fork产生的子进程会继承文件描述符,但是不继承记录锁的;
4.如果对一个文件设置了执行时关闭,那么执行了exec后,父进程会自动释放该文件的记录锁。

建议锁和强制锁

建议锁:
指的是记录锁的控制是由软件决定,要求软件需要按照统一操作规则来操作一个文件,比如对一个文件的读写操作前都先判断记录锁状态是否允许,以此达到锁的目的。建议性锁要求软件控制读写行为,如果一个进程对一个文件加了写锁,而另一个进程不管锁的状态直接写入实际上也是可以成功的,但是这时就达不到锁的目的了。

强制锁:
强制记录锁,就不是简单靠应用程序控制读写行为了,如果设置一个记录锁,那么不需要先判断记录锁,底层读写操作中会自动进行文件记录锁的判断,如果该进程没有获取记录锁,读写是会直接返回失败的。这就是和建议性锁的差异所在。

打开强制性锁的开关,可以通过如下方式:

1.文件属性中的设置组ID位需要置位
2.文件属性中的组执行位需要清除

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/88743394