文章目录
一、基本概念
2.1 基础
- 排他锁(互斥锁): 即同一时刻只有一个对象拥有该锁。
- 共享锁:即同一时刻可以被多个对象拥有。
2.2 文件锁(读锁,写锁)
- 读锁:其实就是共享锁。
- 写锁:其实就是排他锁。
也就是说,同一文件的某个范围内的数据,可以被多个操作者同时读。但是最多只有一个操作者可以同时写。当有操作者在写时,读锁也无法获取。
简单的说就是:
写的时候不可以读。没写的时候可以 同时读。如果已经获取了读锁,则写锁也将无法获取
2.2 加锁范围(实际上就是锁的粒度)
also known as byte-range, file-segment, or file-region locks
引用自:Ubuntu 20.4man fcntl
,说明了锁的范围可以是字节范围,文件段,或者是整个文件范围内的锁。
二、相关函数和数据结构声明
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
cmd 参数的可选项中有,F_SETLK / F_SETLKW / F_GETLK 三项。这些参数与锁的信息获取和设置(加锁解锁)相关。
关于fcntl可以查看:Linux fcntl 函数
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
(set by F_GETLK and F_OFD_GETLK) */
...
};
这里是从ubuntu 20.4版本的man fcntl中得到的。
// fcntl-linux.h 文件中的节选
/* For old implementation of BSD flock. */
#ifndef F_EXLCK
# define F_EXLCK 4 /* or 3 */
# define F_SHLCK 8 /* or 4 */
#endif
从fcntl-linux.h头文件中可以看到l_type除了以上的三种,还有F_SHLCK(共享锁)和F_EXLCK(排他锁)
这里也说了,这两个宏定义只是为了兼容BSD的老的实现。所以,其实F_EXLCK(排他锁)就是F_WRLCK(写锁),F_SHLCK(共享锁)就是F_RDLCK(读锁)。
三、实例
3.1 F_SETLK验证:写的时候不可以读。没写的时候可以同时读。
为了方便验证,我编写了下面的工具代码,可以方便的进行文件加锁验证。
//fcntl_lock.c
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
int
main(int argc, char **args)
{
int fd = 0;
struct flock fl= {
0};
char *str_filename, *str_lock_type, *str_start, *str_len, *str_pid;
int lock_type, start, len, pid;
if (argc < 5) {
printf(
"usage: \n"
" fcntl_lock [filename] [lock_type] [start] [len] [pid]\n"
" lock_type: options is 0(F_RDLCK), 1(O_WRLCK), 2(O_UNLCK)\n"
" start: start is base from the start of the file head\n\n"
" Remark: the lock range is [start, start + len)\n"
" pid: thie param is option, to set the l_pid elem for flock struct\n"
);
exit(-1);
}
str_filename = args[1];
str_lock_type = args[2];
str_start = args[3];
str_len = args[4];
str_pid = argc >= 5 ? args[5] : NULL;
// parse args
lock_type = atoi(str_lock_type);
start = atoi(str_start);
len = atoi(str_len)