flock – 文件锁
函数介绍
flock
系统调用用于对文件进行加锁操作,实现进程间的文件访问同步。文件锁可以防止多个进程同时修改同一文件,保证数据的一致性。
(https://www.calcguide.tech/2025/08/17/flock%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%e5%8f%8a%e7%a4%ba%e4%be%8b/)
函数原型
#include <sys/file.h>
int flock(int fd, int operation);
功能
参数
int fd
: 文件描述符int operation
: 锁操作类型LOCK_SH
: 共享锁(读锁),多个进程可以同时持有LOCK_EX
: 排他锁(写锁),只能有一个进程持有LOCK_UN
: 解锁LOCK_NB
: 非阻塞模式(与上述操作组合使用)
返回值
- 成功时返回0
- 失败时返回-1,并设置errno:
EAGAIN/EWOULDBLOCK
: 非阻塞模式下无法获取锁EBADF
: 文件描述符无效EINTR
: 系统调用被信号中断EINVAL
: 参数无效EOPNOTSUPP
: 文件系统不支持锁
相似函数
fcntl()
: 更灵活的文件锁操作lockf()
: POSIX文件锁接口
示例代码
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main() {
int fd;
pid_t pid;
printf("=== Flock函数示例 ===\n");
// 创建测试文件
fd = open("test_flock.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
exit(EXIT_FAILURE);
}
printf("成功创建测试文件,文件描述符: %d\n", fd);
// 写入初始数据
const char *initial_data = "Initial data for flock test\n";
if (write(fd, initial_data, strlen(initial_data)) == -1) {
perror("写入初始数据失败");
close(fd);
exit(EXIT_FAILURE);
}
// 示例1: 基本的排他锁操作
printf("\n示例1: 基本的排他锁操作\n");
// 获取排他锁
if (flock(fd, LOCK_EX) == -1) {
perror("获取排他锁失败");
} else {
printf(" 成功获取排他锁\n");
// 在锁保护下写入数据
const char *exclusive_data = "Data written with exclusive lock\n";
if (write(fd, exclusive_data, strlen(exclusive_data)) == -1) {
perror(" 写入数据失败");
} else {
printf(" 成功写入数据到文件\n");
}
// 释放锁
if (flock(fd, LOCK_UN) == -1) {
perror(" 释放锁失败");
} else {
printf(" 成功释放排他锁\n");
}
}
// 示例2: 共享锁操作
printf("\n示例2: 共享锁操作\n");
// 获取共享锁
if (flock(fd, LOCK_SH) == -1) {
perror("获取共享锁失败");
} else {
printf(" 成功获取共享锁\n");
// 在锁保护下读取数据
char buffer[200];
lseek(fd, 0, SEEK_SET);
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf(" 读取文件内容:\n%s", buffer);
}
// 释放锁
if (flock(fd, LOCK_UN) == -1) {
perror(" 释放共享锁失败");
} else {
printf(" 成功释放共享锁\n");
}
}
// 示例3: 非阻塞锁操作
printf("\n示例3: 非阻塞锁操作\n");
// 先获取一个排他锁
if (flock(fd, LOCK_EX) == -1) {
perror("获取排他锁失败");
} else {
printf(" 进程已持有排他锁\n");
// 尝试非阻塞获取另一个排他锁(应该失败)
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf(" 非阻塞获取排他锁: 立即返回EAGAIN(锁被占用)\n");
} else {
perror(" 非阻塞获取锁失败");
}
} else {
printf(" 非阻塞获取排他锁成功(不应该发生)\n");
flock(fd, LOCK_UN); // 释放意外获取的锁
}
// 尝试非阻塞获取共享锁(也应该失败,因为有排他锁)
if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf(" 非阻塞获取共享锁: 立即返回EAGAIN(有排他锁)\n");
} else {
perror(" 非阻塞获取共享锁失败");
}
} else {
printf(" 非阻塞获取共享锁成功(不应该发生)\n");
flock(fd, LOCK_UN); // 释放意外获取的锁
}
// 释放锁
flock(fd, LOCK_UN);
printf(" 释放排他锁\n");
}
// 示例4: 多进程锁演示
printf("\n示例4: 多进程锁演示\n");
pid = fork();
if (pid == -1) {
perror("fork失败");
close(fd);
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程
printf(" 子进程 %d 开始执行\n", getpid());
// 子进程打开同一个文件
int child_fd = open("test_flock.txt", O_RDWR);
if (child_fd == -1) {
perror(" 子进程打开文件失败");
exit(EXIT_FAILURE);
}
printf(" 子进程尝试获取排他锁...\n");
if (flock(child_fd, LOCK_EX) == -1) {
perror(" 子进程获取排他锁失败");
} else {
printf(" 子进程成功获取排他锁\n");
// 写入子进程数据
char child_data[100];
sprintf(child_data, "Data from child process %d\n", getpid());
if (write(child_fd, child_data, strlen(child_data)) == -1) {
perror(" 子进程写入数据失败");
} else {
printf(" 子进程写入数据成功\n");
}
sleep(3); // 持有锁3秒
// 释放锁
if (flock(child_fd, LOCK_UN) == -1) {
perror(" 子进程释放锁失败");
} else {
printf(" 子进程释放排他锁\n");
}
}
close(child_fd);
exit(EXIT_SUCCESS);
} else {
// 父进程
sleep(1); // 让子进程先运行
printf(" 父进程 %d 尝试获取排他锁...\n", getpid());
// 父进程尝试获取排他锁(会被阻塞直到子进程释放)
printf(" 父进程获取排他锁(会被阻塞)...\n");
if (flock(fd, LOCK_EX) == -1) {
perror(" 父进程获取排他锁失败");
} else {
printf(" 父进程成功获取排他锁(子进程已释放)\n");
// 写入父进程数据
char parent_data[100];
sprintf(parent_data, "Data from parent process %d\n", getpid());
if (write(fd, parent_data, strlen(parent_data)) == -1) {
perror(" 父进程写入数据失败");
} else {
printf(" 父进程写入数据成功\n");
}
// 释放锁
if (flock(fd, LOCK_UN) == -1) {
perror(" 父进程释放锁失败");
} else {
printf(" 父进程释放排他锁\n");
}
}
// 等待子进程结束
wait(NULL);
}
// 示例5: 共享锁并发演示
printf("\n示例5: 共享锁并发演示\n");
// 创建多个子进程同时获取共享锁
for (int i = 0; i < 3; i++) {
pid = fork();
if (pid == 0) {
// 子进程
int child_fd = open("test_flock.txt", O_RDONLY);
if (child_fd != -1) {
printf(" 子进程 %d 尝试获取共享锁...\n", getpid());
if (flock(child_fd, LOCK_SH) == 0) {
printf(" 子进程 %d 成功获取共享锁\n", getpid());
// 读取数据
char read_buffer[200];
lseek(child_fd, 0, SEEK_SET);
ssize_t bytes_read = read(child_fd, read_buffer, sizeof(read_buffer) - 1);
if (bytes_read > 0) {
read_buffer[bytes_read] = '\0';
printf(" 子进程 %d 读取数据成功\n", getpid());
}
sleep(2); // 持有共享锁2秒
flock(child_fd, LOCK_UN);
printf(" 子进程 %d 释放共享锁\n", getpid());
}
close(child_fd);
}
exit(EXIT_SUCCESS);
}
}
// 等待所有子进程结束
for (int i = 0; i < 3; i++) {
wait(NULL);
}
// 示例6: 锁的继承和关闭行为
printf("\n示例6: 锁的继承和关闭行为\n");
if (flock(fd, LOCK_EX) == 0) {
printf(" 进程持有排他锁\n");
// 复制文件描述符
int dup_fd = dup(fd);
if (dup_fd != -1) {
printf(" 复制文件描述符: %d -> %d\n", fd, dup_fd);
// 使用复制的fd释放锁
if (flock(dup_fd, LOCK_UN) == 0) {
printf(" 使用复制的fd释放锁成功\n");
}
close(dup_fd);
}
// 重新获取锁
if (flock(fd, LOCK_EX) == 0) {
printf(" 重新获取锁成功\n");
flock(fd, LOCK_UN);
}
}
// 示例7: 错误处理演示
printf("\n示例7: 错误处理演示\n");
// 尝试对无效文件描述符加锁
if (flock(999, LOCK_EX) == -1) {
printf(" 对无效文件描述符加锁: %s\n", strerror(errno));
}
// 清理资源
printf("\n清理资源...\n");
if (close(fd) == -1) {
perror("关闭文件失败");
} else {
printf("成功关闭文件描述符 %d\n", fd);
}
// 删除测试文件
if (unlink("test_flock.txt") == -1) {
perror("删除测试文件失败");
} else {
printf("成功删除测试文件\n");
}
return 0;
}
}
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main() {
int fd;
pid_t pid;
printf("=== Flock函数示例 ===\n");
// 创建测试文件
fd = open("test_flock.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
exit(EXIT_FAILURE);
}
printf("成功创建测试文件,文件描述符: %d\n", fd);
// 写入初始数据
const char *initial_data = "Initial data for flock test\n";
if (write(fd, initial_data, strlen(initial_data)) == -1) {
perror("写入初始数据失败");
close(fd);
exit(EXIT_FAILURE);
}
// 示例1: 基本的排他锁操作
printf("\n示例1: 基本的排他锁操作\n");
// 获取排他锁
if (flock(fd, LOCK_EX) == -1) {
perror("获取排他锁失败");
} else {
printf(" 成功获取排他锁\n");
// 在锁保护下写入数据
const char *exclusive_data = "Data written with exclusive lock\n";
if (write(fd, exclusive_data, strlen(exclusive_data)) == -1) {
perror(" 写入数据失败");
} else {
printf(" 成功写入数据到文件\n");
}
// 释放锁
if (flock(fd, LOCK_UN) == -1) {
perror(" 释放锁失败");
} else {
printf(" 成功释放排他锁\n");
}
}
// 示例2: 共享锁操作
printf("\n示例2: 共享锁操作\n");
// 获取共享锁
if (flock(fd, LOCK_SH) == -1) {
perror("获取共享锁失败");
} else {
printf(" 成功获取共享锁\n");
// 在锁保护下读取数据
char buffer[200];
lseek(fd, 0, SEEK_SET);
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf(" 读取文件内容:\n%s", buffer);
}
// 释放锁
if (flock(fd, LOCK_UN) == -1) {
perror(" 释放共享锁失败");
} else {
printf(" 成功释放共享锁\n");
}
}
// 示例3: 非阻塞锁操作
printf("\n示例3: 非阻塞锁操作\n");
// 先获取一个排他锁
if (flock(fd, LOCK_EX) == -1) {
perror("获取排他锁失败");
} else {
printf(" 进程已持有排他锁\n");
// 尝试非阻塞获取另一个排他锁(应该失败)
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf(" 非阻塞获取排他锁: 立即返回EAGAIN(锁被占用)\n");
} else {
perror(" 非阻塞获取锁失败");
}
} else {
printf(" 非阻塞获取排他锁成功(不应该发生)\n");
flock(fd, LOCK_UN); // 释放意外获取的锁
}
// 尝试非阻塞获取共享锁(也应该失败,因为有排他锁)
if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf(" 非阻塞获取共享锁: 立即返回EAGAIN(有排他锁)\n");
} else {
perror(" 非阻塞获取共享锁失败");
}
} else {
printf(" 非阻塞获取共享锁成功(不应该发生)\n");
flock(fd, LOCK_UN); // 释放意外获取的锁
}
// 释放锁
flock(fd, LOCK_UN);
printf(" 释放排他锁\n");
}
// 示例4: 多进程锁演示
printf("\n示例4: 多进程锁演示\n");
pid = fork();
if (pid == -1) {
perror("fork失败");
close(fd);
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程
printf(" 子进程 %d 开始执行\n", getpid());
// 子进程打开同一个文件
int child_fd = open("test_flock.txt", O_RDWR);
if (child_fd == -1) {
perror(" 子进程打开文件失败");
exit(EXIT_FAILURE);
}
printf(" 子进程尝试获取排他锁...\n");
if (flock(child_fd, LOCK_EX) == -1) {
perror(" 子进程获取排他锁失败");
} else {
printf(" 子进程成功获取排他锁\n");
// 写入子进程数据
char child_data[100];
sprintf(child_data, "Data from child process %d\n", getpid());
if (write(child_fd, child_data, strlen(child_data)) == -1) {
perror(" 子进程写入数据失败");
} else {
printf(" 子进程写入数据成功\n");
}
sleep(3); // 持有锁3秒
// 释放锁
if (flock(child_fd, LOCK_UN) == -1) {
perror(" 子进程释放锁失败");
} else {
printf(" 子进程释放排他锁\n");
}
}
close(child_fd);
exit(EXIT_SUCCESS);
} else {
// 父进程
sleep(1); // 让子进程先运行
printf(" 父进程 %d 尝试获取排他锁...\n", getpid());
// 父进程尝试获取排他锁(会被阻塞直到子进程释放)
printf(" 父进程获取排他锁(会被阻塞)...\n");
if (flock(fd, LOCK_EX) == -1) {
perror(" 父进程获取排他锁失败");
} else {
printf(" 父进程成功获取排他锁(子进程已释放)\n");
// 写入父进程数据
char parent_data[100];
sprintf(parent_data, "Data from parent process %d\n", getpid());
if (write(fd, parent_data, strlen(parent_data)) == -1) {
perror(" 父进程写入数据失败");
} else {
printf(" 父进程写入数据成功\n");
}
// 释放锁
if (flock(fd, LOCK_UN) == -1) {
perror(" 父进程释放锁失败");
} else {
printf(" 父进程释放排他锁\n");
}
}
// 等待子进程结束
wait(NULL);
}
// 示例5: 共享锁并发演示
printf("\n示例5: 共享锁并发演示\n");
// 创建多个子进程同时获取共享锁
for (int i = 0; i < 3; i++) {
pid = fork();
if (pid == 0) {
// 子进程
int child_fd = open("test_flock.txt", O_RDONLY);
if (child_fd != -1) {
printf(" 子进程 %d 尝试获取共享锁...\n", getpid());
if (flock(child_fd, LOCK_SH) == 0) {
printf(" 子进程 %d 成功获取共享锁\n", getpid());
// 读取数据
char read_buffer[200];
lseek(child_fd, 0, SEEK_SET);
ssize_t bytes_read = read(child_fd, read_buffer, sizeof(read_buffer) - 1);
if (bytes_read > 0) {
read_buffer[bytes_read] = '\0';
printf(" 子进程 %d 读取数据成功\n", getpid());
}
sleep(2); // 持有共享锁2秒
flock(child_fd, LOCK_UN);
printf(" 子进程 %d 释放共享锁\n", getpid());
}
close(child_fd);
}
exit(EXIT_SUCCESS);
}
}
// 等待所有子进程结束
for (int i = 0; i < 3; i++) {
wait(NULL);
}
// 示例6: 锁的继承和关闭行为
printf("\n示例6: 锁的继承和关闭行为\n");
if (flock(fd, LOCK_EX) == 0) {
printf(" 进程持有排他锁\n");
// 复制文件描述符
int dup_fd = dup(fd);
if (dup_fd != -1) {
printf(" 复制文件描述符: %d -> %d\n", fd, dup_fd);
// 使用复制的fd释放锁
if (flock(dup_fd, LOCK_UN) == 0) {
printf(" 使用复制的fd释放锁成功\n");
}
close(dup_fd);
}
// 重新获取锁
if (flock(fd, LOCK_EX) == 0) {
printf(" 重新获取锁成功\n");
flock(fd, LOCK_UN);
}
}
// 示例7: 错误处理演示
printf("\n示例7: 错误处理演示\n");
// 尝试对无效文件描述符加锁
if (flock(999, LOCK_EX) == -1) {
printf(" 对无效文件描述符加锁: %s\n", strerror(errno));
}
// 清理资源
printf("\n清理资源...\n");
if (close(fd) == -1) {
perror("关闭文件失败");
} else {
printf("成功关闭文件描述符 %d\n", fd);
}
// 删除测试文件
if (unlink("test_flock.txt") == -1) {
perror("删除测试文件失败");
} else {
printf("成功删除测试文件\n");
}
return 0;
}