文件锁锁定的是整个文件,而记录锁定还可以锁定文件的某一特定部分,即从文件的某一相对位置开始的一段连续的字节流。
当一个进程正在读取或者修改文件的某个部分时,使用文件记录锁可以阻止其他进程修改同一文件的相同区域。它可以用来锁定文件的某个区域或者整个文件,SylixOS 支持多种文件记录锁 API。
注:SylixOS 支持多种设备驱动模型,但是目前只有 NEW_1 型设备驱动支持文件记录锁功能,此类驱动文件节点类似于UNIX 系统的 vnode。
SylixOS可以通过fcntl 函数操作文件记录锁的功能。
#include<fcntl.h> int fcntl (int iFd, int iCmd, ...) |
函数fcntl原型分析:
1. 此函数成功时根据参数iCmd的不同而返回不同的值,失败返回-1并设置错误号;
2. 参数 iFd 是文件描述符;
3. 参数 iCmd 是命令;
4. 参数 ...是命令参数。
fcntl设置文件记录锁时iCmd对应3个命令:F_GETLK、F_SETLK 和 F_SETLKW。命令解释分别是:F_GETLK表示获取文件锁;F_SETLK表示设置文件锁(非阻塞);F_SETLKW表示设置文件锁(阻塞)。第 3 个参数是一个 flock 结构体指针,结构体成员如程序清单 2-1所示。
程序清单 2-1 flock结构体成员
struct flock { short l_type; /* F_RDLCK, F_WRLCK, or F_UNLCK */ short l_whence; /* flag to choose starting */ /* offset */ off_t l_start; /* relative offset, in bytes */ off_t l_len; /* length, in bytes; 0 means */ /* lock to EOF */ pid_t l_pid; /* returned with F_GETLK */ long l_xxx[4]; /* reserved for future use */ }; |
l_type表示锁的类型分别为:F_RDLOCK(共享读锁)、F_WRLOCK(独占写锁)和F_UNLCK(解锁);
l_whence表示文件记录锁的起始位置,其值如图 2-1所示。
图 2-1 l_Whence值相关
iWhence值 | oftOffset说明 |
SEEK_SET | 将文件的偏移量设置为距文件开始处 oftOffset 个字节 |
SEEK_CUR | 将文件的偏移量设置为当前值加oftOffset个字节,oftOffset可为负 |
SEEK_END | 将文件的偏移量设置为文件长度加oftOffset个字节,oftOffset可为负 |
l_start是相对l_whence偏移开始位置(注意不可以从文件开始的之前部分锁起);
l_len是锁定区域长度,如果为0则锁定文件尾(EOF),如果向文件中追加数据也将被锁;
l_pid是已占用锁的进程ID(由命令F_GETLK返回)。
文件记录锁中的F_RDLOCK(共享读锁)和F_WRLOCK(独占写锁)的基本规则是:任意多个进程在一个给定字节上可以有一把共享的读锁,但是在一个给定字节上只能有一个进程有一把独占的写锁。进一步而言,如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个给定字节上有一把写锁,则不能再加任何锁。基本规则如表 2-1所示。
表 2-1 记录锁规则
当前字节区锁状态 | 请求 | |
读锁 | 写锁 | |
无锁 | 允许 | 允许 |
一个或多个读锁 | 允许 | 拒绝 |
一个写锁 | 拒绝 | 拒绝 |
上面的规则适用于不同进程提出的锁请求,并不适用于单个进程提出的锁请求。也就是说,如果一个进程对一个文件区间已经有了一把锁,后来该进程又企图在同一个区间再加一把锁,那么也是可以的,这个时候新锁将替换已有锁。因此,如果一个进程将某个文件加了一把写锁,然后又企图给文件加一把读锁,那么将会成功执行,原来的写锁会被替换为读锁。
记录锁采用(pid,start,end)三元组作为锁标识,一个文件可拥有多个记录锁,同一区域只允许有一个记录锁。
当进程终止(正常/不正常),该进程拥有的所有记录锁都将释放。同一个进程中,指向同一文件(i-node)的fd都可以操作该文件上的记录锁:如释放、修改等。显式调用F_UNLCK和close(fd)都将释放锁,close将释放整个文件中该进程拥有的所有记录锁。
记录锁不被spawn的子进程继承(PID不同)。
记录锁的类型转换、改变锁范围等操作均为原子操作。
未设置FD_CLOEXEC时,记录锁将被exec后的进程继承(PID相同)。
记录锁对文件打开mode有要求:加读锁要求文件句柄fd有读权限,加写锁要求fd有写权限。
比如进程A对文件“/apps/file”加上写锁(进程A先上锁),当A进程用户操作结束后会释放锁给进程B操作,进程A代码如程序清单 3-1所示。
程序清单 3-1 进程A代码
#include<stdio.h> #include<unistd.h> #include<fcntl.h>
#define FILE_PATH "/apps/file" intmain(intargc, char *argv[]) { int iFd = 0; struct flock flck; short sLockt = F_WRLCK;
iFd = open(FILE_PATH, O_RDWR); /*打开文件*/ if (iFd < 0) { fprintf(stderr, "open file failed.\n"); return -1; } /* * l_whence = SEEK_SET;l_start = 0;表示从文件开始起偏移量为0开始上锁 * l_len = 0;表示锁定到文件尾 */ flck.l_type = sLockt; /* 文件记录锁类型设置为独写锁 */ flck.l_whence = SEEK_SET; flck.l_start = 0; flck.l_len = 0;
if (fcntl(iFd, F_SETLK, &flck) < 0) { /* fcntl设置文件记录锁 */ fprintf(stderr, "add write lock failed.\n"); close(iFd); return -1; } /* * 用户对文件被锁定区域操作 */ sLockt = F_UNLCK; /* 文件记录锁类型设置为解锁 */ flck.l_type = sLockt; flck.l_whence = SEEK_SET; flck.l_start = 0; flck.l_len = 0;
if (fcntl(iFd, F_SETLK, &flck) < 0) { fprintf(stderr, "unlock failed.\n"); close(iFd); return -1; } close(iFd); return 0; } |
进程B也对文件“/apps/file”操作,区别是进程B设置为F_SETLKW(阻塞等待解锁)。进程B阻塞等待进程A解锁方可对文件进行操作,进程B代码如程序清单 3-2所示。
程序清单 3-2 进程B代码
#include<stdio.h> #include<unistd.h> #include<fcntl.h>
#define FILE_PATH "/apps/file" intmain(intargc, char *argv[]) { int iFd = 0; struct flock flck; short sLockt = F_WRLCK;
iFd = open(FILE_PATH, O_RDWR); /* 打开文件 */ if (iFd < 0) { fprintf(stderr, "open file failed.\n"); return -1; } /* * l_whence = SEEK_SET;l_start = 0;表示从文件开始起偏移量为0开始上锁 * l_len = 0;表示锁定到文件尾 */ flck.l_type = sLockt; /* 文件记录锁类型设置为独写锁 */ flck.l_whence = SEEK_SET; flck.l_start = 0; flck.l_len = 0;
if (fcntl(iFd, F_SETLKW, &flck) < 0) { /* fcntl设置文件记录锁 */ fprintf(stderr, "add write lock failed.\n"); close(iFd); return -1; } /* * 用户对文件被锁定区域进行操作 */ close(iFd); return 0; } |
本文出自 “12548035” 博客,请务必保留此出处http://12558035.blog.51cto.com/12548035/1916215
原文地址:http://12558035.blog.51cto.com/12548035/1916215