task1: 验证Linux不会自动给文件加锁
先说结论,结论是不会
我写了一个这样的程序
#include <stdio.h>
#include <unistd.h>
int main() {
const char* pathname = "your_file_pathname.txt";
FILE* file = NULL;
int count = 100;
if(access(pathname, F_OK) == 0) {
file = fopen(pathname, "r+");
printf("open in r+ mode\n");
}
else {
file = fopen(pathname, "w+");
printf("open in w+ mode\n");
}
if (file == NULL) {
printf("无法打开文件\n");
return 1;
}
fseek(file, 0, SEEK_END); // 将文件指针移动到文件末尾
long file_size = ftell(file); // 获取文件大小
if (file_size == 0) {
fprintf(file, "0\n"); // 文件为空,写入0
fflush(file); // 刷新文件缓冲区,确保写入文件
}
fseek(file, 0, SEEK_SET); // 将文件指针移动到文件开头
int num;
fscanf(file, "%d", &num); // 读取文件中的整数
printf("num = %d\n", num);
sleep(2);
while(count--) {
num++; // 将整数加1
fseek(file, 0, SEEK_SET); // 将文件指针移动到文件开头
fprintf(file, "%d\n", num); // 将更新后的整数写回文件
fflush(file); // 刷新文件缓冲区,确保写入文件
fseek(file, 0, SEEK_SET); // 将文件指针移动到文件开头
fscanf(file, "%d", &num); // 读取文件中的整数
printf("num = %d\n", num);
sleep(2);
}
fclose(file); // 关闭文件
return 0;
}
上面这个程序会读取文件中的数字,然后给数字+1,再写回文件
这个程序里没有给文件加锁,我同时运行了 8 个这样的程序,最后的 result file 里的数字是 127,而非 800,说明 Linux 本身并不会给文件加锁
task2: 如何手动给文件加锁?
首先根据 ChatGPT,我们可以获得如下代码(经过部分注释和修改,可以根据注释理解源码):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
// 打开文件
fd = open("file.txt", O_WRONLY);
if (fd == -1) {
perror("open");
exit(1);
}
// 设置文件锁
// 我们定义了一个struct flock结构体来描述文件锁的属性,包括锁的类型、起始位置和长度。
struct flock lock;
lock.l_type = F_WRLCK; // 写锁
// short int l_whence; /* Where `l_start' is relative to (like `lseek'). */
// __off_t l_start; /* Offset where the lock begins. */
// l_whence 和 l_start 是共同指定锁的起始位置的
lock.l_whence = SEEK_SET;
lock.l_start = 0;
// __off_t l_len; /* Size of the locked area; zero means until EOF. */
lock.l_len = 0; // 锁定整个文件
// 接下来,我们使用fcntl函数来获取文件锁,使用F_SETLKW标志表示在获取锁时阻塞进程,直到锁可用。
// # define F_SETLKW 7 /* Set record locking info (blocking). */
if (fcntl(fd, F_SETLKW, &lock) == -1) {
perror("fcntl");
exit(1);
}
// 在文件中写入数据 NOTE: 关键区域
// ...
// 在写入数据完成后,我们再次使用fcntl函数来释放文件锁,使用F_SETLK标志表示释放锁。
// # define F_RDLCK 0 /* Read lock. */
// # define F_WRLCK 1 /* Write lock. */
// # define F_UNLCK 2 /* Remove lock. */
// 根据手册来看,F_SETLKW 是阻塞式获取/释放锁,F_SETLK是非阻塞获取/释放锁
lock.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &lock) == -1) {
perror("fcntl");
exit(1);
}
// 关闭文件
close(fd);
return 0;
}
根据手册阅读,如下:
只需要修改 lock.l_type = F_UNLCK; 就可以决定是上锁还是释放锁。F_SETLK 和 F_SETLKW 只是决定 阻塞/非阻塞 获取/释放 锁
我们做个实验看看,“写另外一个文件2,获取锁后不释放,文件1分别使用 阻塞/非阻塞 方式获取锁,看是否如手册所描述一般行为”
经过测试,当 Holding lock 的程序被强制退出时,它所持有的锁也会被强制释放
测试1:blocking 阻塞式获取锁
首先我们测试,先使用 norelease.c 文件获取锁,随后不退出
接着再使用 blocking.c 获取锁,可以发现会阻塞再这个地方
以下是 blocking.c 源码:
可以看到第36行的 printf 并没有被执行,blocking.c 验证完毕
测试2:nonblocking 非阻塞式获取锁
TODO:here