概述
文件锁是一种可以对文件部分或整体加锁的工具
对于一部分的应用程序而言 为了保证一个进程只能对一个文件进行读写操作 而必须加以文件锁的方式保证数据的正确
uc提供了两个文件锁函数:flock和fcntl
早期伯克利版本只支持了flock,只能对整个文件加锁,这对效率来说不太友好
而fccntl和lockf可以对部分进行加锁下面会介绍
1、flock
这里只简单介绍一下flock因为现在大部分使用fcntl就可以完成对文件的加锁且更灵活
int flock(int fd, int operation);
//其中fd是文件描述符。
operation:
1.LOCK_SH :共享锁
2.LOCK_EX :排他锁或者独占锁
3.LOCK_UN : 解锁。
4.LOCK_NB:非阻塞(与以上三种操作一起使用)
2、fcntl
#include <fcntl.h>
int fcntl (int fd, int cmd, struct flock* lock);
struct flock {
short int l_type; // 锁的类型:
// F_RDLCK/F_WRLCK/F_UNLCK
// (读锁/写锁/解锁)
short int l_whence; // 偏移起点:
// SEEK_SET/SEEK_CUR/SEEK_END
// (文件头/当前位置/文件尾)
off_t l_start; // 锁区偏移,从l_whence开始
off_t l_len; // 锁区长度,0表示锁到文件尾
pid_t l_pid; // 加锁进程,-1表示自动设置
};
cmd三大取值
F_GETLK - 测试lock所表示的锁是否可加。 测试模式
若可加则将lock.l_type置为F_UNLCK,
否则通过lock返回当前锁的信息。F_SETLK - 设置锁定状态为lock.l_type, 不阻塞加锁
成功返回0,失败返回-1。
若因其它进程持有锁而导致失败,
则errno为EACCES或EAGAIN。F_SETLKW - 设置锁定状态为lock.l_type, 阻塞加锁
成功返回0,否则一直等待,
除非被信号打断返回-1。
操作锁代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
//控制台读取锁的信息
void read_lock(struct flock *lock){
printf("输入锁的起始位置参考点(SEEK_SET 0,SEEK_CUR 1,SEEK_END 2):");
int whence = 0;
scanf("%d",&whence);
lock->l_whence = whence==0?SEEK_SET:(whence==1?SEEK_CUR:SEEK_END);
printf("输入距离参考位置偏移距离:");
off_t offset = 0;
scanf("%ld",&offset);
lock->l_start = offset;
printf("请输入锁的长度:");
off_t len = 0;
scanf("%ld",&len);
lock->l_len = len;
lock->l_pid = -1;//自动填充该进程ID
}
void write_lock(struct flock *lock){//读出锁的五大信息
if(lock->l_type == F_RDLCK){
printf("读锁 ");
}else{
printf("写锁 ");
}
int w = lock->l_whence;
printf("锁的参考位置:%s ",w==SEEK_SET?"SET":(w==SEEK_CUR?"CUR":"END"));
printf("偏移参考位置距离:%ld ",lock->l_start);
printf("锁的长度:%ld ",lock->l_len);
printf("上锁进程为:%d\n",lock->l_pid);
}
void menu(void){
printf("***文件锁功能测试***\n");
printf("** 1.上读锁 \n");
printf("** 2.测试是否可以上读锁 \n");
printf("** 3.上写锁 \n");
printf("** 4.测试是否可以上写锁 \n");
printf("** 5.解锁 \n");
printf("** 0.退出 \n");
printf(">>>");
}
void run(void){
int fd = open("a.txt",O_RDWR);//手动创建a.txt
if(fd == -1){
printf("%m\n");
return;
}
printf("进程号:%d\n",getpid());//打印当前进程的进程号!
struct flock lock = {};//定义一把锁
//一般都需要定义一把锁 填写锁结构体的信息 随后插入到fd锁指向的文件中
//做检查操作时 struct flock 类型可以接受信息
for(;;){
menu();
int opt = 0;
scanf("%d",&opt);
if(opt>=1 && opt<=4){
read_lock(&lock);//填写锁信息
switch(opt){
case 1:
case 2:lock.l_type = F_RDLCK;break;
case 3:
case 4:lock.l_type = F_WRLCK;break;
}
int ret = 0;
if(opt==2||opt==4){
ret = fcntl(fd,F_GETLK,&lock);//测试是否可以上锁
//fcntl 第二个参数F_GETLK是获取fd文件的lock信息
//返回值如果是0 读取成功
//如果被上锁 lock的信息会被修改为上锁的信息
//如果没有被上锁 lock的l_type会被修改为F_UNLCK
if(ret == 0){
if(lock.l_type == F_UNLCK){
printf("可以上锁!\n");
}else{
printf("不可以上锁!\n");
write_lock(&lock);
}
}else{
printf("获得锁信息失败!\n");
}
}else{
printf("如果不能上锁,是否一直等待(0.等 1.立即返回):");
int w = 0;
scanf("%d",&w);
int cmd = w==0?F_SETLKW:F_SETLK;
ret = fcntl(fd,cmd,&lock);
if(ret == 0){
printf("上锁成功!\n");
}else{
printf("上锁失败!\n");
if(errno == EAGAIN || errno == EACCES){
printf("其他进程已上锁!\n");
}
}
}
}else if(opt==5){
lock.l_type = F_UNLCK;//解锁
printf("请输入解锁的信息:\n");
read_lock(&lock);
int ret = fcntl(fd,F_SETLK,&lock);
if(ret == 0){
printf("解锁成功!\n");
}else{
printf("解锁失败!\n");
}
}else if(opt == 0){
break;
}else{
printf("没有该操作!\n");
}
}
close(fd);
}
int main(){
run();
return 0;
}