文件锁
2018-08-15 23:08 linux
锁定文件的两个接口:
- flock(), 用来锁定整个文件
- fcntl(), 用来锁定文件的区域
基本用法是:
- 给文件加锁
- 一般的文件读写操作
- 释放之前的锁
flock()
文件描述符关闭之后, 这个文件锁自动被释放.
flock() 加的锁, 是放在了内核内部的 file description结构体里的, 而不是文件描述符 (file descriptor) 里. 这样一来, 如果使用 dup2(), fcntl() 等方式复制了文件描述符 的话, 这两个文件描述符都指写同一个加了锁的 file description. 在使用 fork() 创建 子进程时, 要特别注意在子进程中应该把锁释放掉.
flock() 有以下不足:
- 只能锁定整个文件
- 只是建议锁, 不是强制锁, 即使进程对一个文件加锁失败了, 它仍可以正常去读写.
- NFS 文件系统对它的支持有限.
fcntl()
可以对整个文件或者部分区域加锁. 基本的调用方式是:
struct flock flockstr;
fcntl(fd, cmd, &flockstr);
以上方法通过修改 flock 结构体来完成加锁和解锁的操作. 该结构体的定义是:
struct flock {
// 锁类型, F_RDLCK, F_WRLCK, F_UNLCK.
short l_type;
// 如何解析 l_start, 可以是 Seek_SET, SEEK_CUR 以及 SEEK_END.
short l_whence;
// 文件锁的起点.
off_t l_start;
// 要锁定的长度, 如果是0, 表示"到文件结尾.
off_t l_len;
// 哪个进程在阻止我们加锁.
pid_t l_pid;
内核会自动检查两个进程使用 fcntl() 导致的死锁问题, 通过让其中的一个进程加锁失败.
使用文件锁来实现单实例进程.
// Copyright (c) 2018 Xu Shaohua <shaohua@biofan.org>.
// All rights reserved.
// Use of this source is governed by General Public License that can be found
// in the LICENSE file.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
long createPidFile(const char* pid_file) {
// Add O_CLOEXEC to release lock in child process after fork().
int fd = open(pid_file, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open()");
return -1;
}
struct flock flockstr;
flockstr.l_type = F_WRLCK;
flockstr.l_whence = SEEK_SET;
flockstr.l_start = 0;
flockstr.l_len = 0;
if (fcntl(fd, F_SETLK, &flockstr) == -1) {
perror("fcntl(F_SETLK)");
// Get process id by which this pid file is locked.
if (fcntl(fd, F_GETLK, &flockstr)) {
perror("fcntl(F_GETLK)");
}
return flockstr.l_pid;
}
return 0;
}
int main(int argc, char* argv[]) {
const size_t kBufSize = 255;
char buf[kBufSize];
if (snprintf(buf, kBufSize, "/tmp/%s-%d.pid", argv[0], getuid()) == -1) {
perror("snprintf()");
exit(EXIT_FAILURE);
}
const long ret = createPidFile(buf);
if (ret == -1) {
printf("Failed to create pid file");
exit(EXIT_FAILURE);
} else if (ret == 0) {
printf("Create pid file successfully, pid: %ld\n", (long) getpid());
} else {
printf("Progress already running with pid: %ld\n", ret);
exit(EXIT_FAILURE);
}
sleep(30);
printf("Release lock and exit process\n");
// No need to release write lock, as it will be released by kernel.
exit(EXIT_SUCCESS);
}