文件锁定
当多个用户同时操作同一个文件时,为了避免共享资源产生竞争状态,我们可以给文件上锁。文件锁分为建议性锁和强制性锁。建议性锁会给文件设置一个标志位,但是,如果对文件操作之前不对该标志位进行检测,那么建议性锁就会形同虚设,其他进程依旧可以对文件进行操作。而强制性锁会让上锁的进程“霸占”这个文件的一些权限,比如一个进程A对文件F设置了一个只允许读操作的强制性锁(读取锁),那么进程B就只能对文件F进行读取,而不能写入或执行,这种锁定状态会一直持续到进程A给文件F解锁。强制性锁可以避免对文件的破坏性操作。当我们使用open、read、write等函数操作文件时,内核都会检测该文件是否被加了强制性锁,如果是,则会导致操作失败。除了锁定文件,文件的某一个状态记录也可以被上锁,称为记录锁。
函数和参数
#include <unistd.h>
#include <fcntl.h>
/**
* @description 默认建议性锁,开启强制性锁需要对系统进行设置
* @param fd 文件描述符
* @param cmd 上锁的方式
* @param lock 上各种锁,锁住哪里
* @return int 执行成功返回0,执行失败返回-1
*/
int fcntl(int fd, int cmd, struct flock* lock);
cmd | 作用 |
---|---|
F_GETLK | 根据lock描述,决定是否上锁 |
F_SETLK | 根据lock描述,设置锁 |
结构体flock的定义如下:
struct flock{
short int l_type; // 锁定状态
short int l_whence; // 锁定的起始位置
off_t l_start; // 起始位置相对于l_whence的偏移量
off_t l_len; // 锁定区域的大小
pid_t l_pid; // 执行锁定动作的进程
}
l_type | 作用 |
---|---|
F_RDLCK | 读取锁,多个进程可以同时建立读取锁 |
F_WRLCK | 写入锁,任何时刻只有一个进程可以建立写入锁 |
F_UNLCK | 解除锁定 |
建议性锁
为了测试建议性锁,我们需要两个程序,程序"lockFile.cpp"负责给文件上锁,并持续占用文件;程序"writeFile.cpp"负责检测建议锁,并向文件中写入内容。由于linux默认建议性锁,所以不需要对系统进行额外配置。
"lockFile.cpp"的内容如下:
/**
* file name: lockFile.cpp
*/
#include <fcntl.h>
#include <stdio.h>
#include <error.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char* argv[]){
struct flock lock;
int res, fd = open("myFile.txt", O_RDWR|O_CREAT, S_IRWXU);
if(fd > 0){
lock.l_type = F_WRLCK; // 写入锁
lock.l_whence = SEEK_SET; // 文件起始位置
lock.l_start = 0; // 不偏移
lock.l_len = 0; // 当len=0时代表锁定到文件的末尾;len=非零正整数时代表锁定的字符数
lock.l_pid = getpid(); // 获得当前进程pid号
res = fcntl(fd, F_SETLK, &lock); // 设置写入锁,不允许其他进程写入
if(res==0){
printf("file has been locked. pid:%d\n", getpid());
while(true); // 保持锁定占用状态
}
}
return 0;
}
运行程序后,终端会陷入while函数中,同时输出该进程的PID值为1078。
我们打开另一个终端,键入命令,查看该进程是否对文件加锁:
$ sudo cat /proc/locks | grep 1078
2: POSIX ADVISORY WRITE 1078 08:20:3446 0 EOF
从输出中可以看出,该进程加了一个写入锁。
而后,我们通过"writeFile.cpp"文件向"myFile.txt"中写入内容。
#include <fcntl.h>
#include <stdio.h>
#include <error.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char* argv[]){
struct flock lock;
int res, fd = open("myFile.txt", O_RDWR|O_CREAT, S_IRWXU);
if(fd > 0){
char buf[] = "I have an apple.";
int count = strlen(buf);
int size = write(fd, buf, count);
if(size==-1){
printf("写入失败.\n");
}
else{
printf("写入成功.\n");
}
close(fd);
}
return 0;
}
执行后发现,可以向"myFile.txt"中写入内容。这就证明了,虽然我们给文件加了写入锁,但由于该锁的性质是建议锁,所以其他进程仍然可以向文件中写入内容。
强制性锁
若要使用强制性锁,需要在root权限下通过mount命令,用-o mand选项来打开该机制。
我们先将"lockFile.cpp"程序终止掉,然后在终端中执行:
$ sudo mount -o remount,mand /
然后重复建议性锁的步骤,发现"writeFile.cpp"无法向"myFile.txt"中写入内容。