fcntl与文件锁

最近要加一个文件同步的服务,以daemon的方式部署在每台机器上,其他服务进程如果需要访问本机磁盘文件时,如果访问不到需要告诉文件同步服务去远端服务器把文件同步过来,同步文件的服务我们直接对rsync做了一层包装,文件同步存在一个中间状态,即文件已经存在但内容还没同步完成,这个中间状态时其他的进程不能来读取文件,这里我们使用了建议性文件锁来协调进程间文件的读取。

文件锁(File Lock)是一种在特定的时间内只允许一个 进程进行访问文件的机制,通过使用文件锁,可以使得多进程访问文件更加安全

一个锁结构如下

1
2
3
4
5
6
7
8
struct flock {
short l_type; /*锁类型*/
short l_whence; /*锁的起始位置*/
off_t l_start; /*锁区域开始位置的字节偏移量*/
off_t l_len; /*锁长度*/
pid_t l_pid; /*持有锁的进程*/

}

l_type的三个取值分别是

  • F_RDLCK 共享读锁
  • F_WRLCK 独占性写锁
  • F_UNLCK 解锁一个区域

l_whence有三个取值

  • SEEK_SET 文件头
  • SEEK_CUR 当前位置
  • SEEK_END 文件尾

在linux操作系统中,我们可以使用fcntl支持的三种命令F_GETLK,F_SETLK和F_SETLKW,通过第三个参数指向一个锁结构的指针(flockptr),用于操作文件描述符实现文件锁。

1
int fcntl(int fd, int cmd, ... /* args */ );

  • F_GETLK 判断flockptr指向的锁是否被另外一把锁排斥,如果已经存在一把锁,这现有的flockptr指向的锁将被阻止创建,并且flockptr指向的锁结构内容将被该锁替换,如果不存在则flockptr指向的锁类型会被设置为 F_UNLOCK。
  • F_SETLK 设置flockptr所指向的锁,如果设置的是读锁或者写锁被兼容性规则阻止的话,fcntl会立即返回错误,如果设置的是F_UNLCK,则可以清除文件锁。
  • F_SETLKW 是F_SETLK的阻塞版本。

需要注意的是F_GETLK测试能否建立一把锁后,使用F_SETLK加锁仍有可能失败,因为两者不是一个原子操作,需要处理F_SETLK是fcntl的返回值。

理解了上面的API后,使用lua对fcntl做了一层封装,代码在lua-fie-lock, 效果如下

1
2
3
4
5
6
7
8
[lua-file-lock]$ lua sample/set_lock.lua 
1539930102 : locked
1539930107 : unlocked

[lua-file-lock]$ lua sample/try_lock.lua
1539930103 : locked, retry after 1s
1539930105 : locked, retry after 1s
1539930107 : lockable

您的支持将鼓励我继续创作!