概念
- 目标
解决多个进程对同一个文件操作产生的竞争状态 - 解决方案:
文件锁
按功能分可分为:读锁和写锁
-
读锁
文件描述符必须以读的方式打开。
一个进程对这个文件设置读锁,其他进程也可以设置读锁,在不改变文件内容的情况下各个进程不存在竞争状态 -
写锁
文件描述符必须写打开。
一个进程对这个文件设置了写锁,其他进程不能在这个文件上设置读锁和写锁,直至给文件设置写锁的进程解锁,其他进程才可以进行设置锁按类型分可分为:建议锁和强制锁
-
强制锁
SVR4的一种锁机制。如果文件设置了调整组ID位,当对这个文件调用fcntl函数设置锁时,则称这种情况下的锁为强制锁。在强制锁的机制下,内核会拒绝一切与锁有冲突的文件操作 -
建议锁
建议锁由fcntl函数提供所有的POSIX兼容的系统都支持建议锁。正如其名这种锁是建议性的,不管文件是否上了锁,程序都可以忽略直接进行读写数据;是由用户进程自愿执行的,进程可以设置锁,但只有当协同工作的进程自愿地查看锁时才对文件有保护作用,内核并不对建议锁内部的强制保护或者检查。在建议锁的机制下文件的互斥访问都是通过fcntl函数来进行置锁,而read()和write()函数并不检查所读写的文件部分是否存在锁
-
实现
fcntl函数
注:F_SETLKW为F_SETLK的一个阻塞版本;即前面两种为非阻塞版本,后面那种为阻塞版本。flock结构体
注:
实践代码
主函数:
#include "io.h"
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
int main(int argc, char *argv[])
{
if(argc<4)
{
printf("usage :%s content file locktype\n",argv[0]);
exit(1);
}
ssize_t size=strlen(argv[1])*sizeof(char);
int fd=open(argv[2],O_WRONLY | O_CREAT, 0777);
if(fd <0)
{
perror("open file error");
exit(1);
}
sleep(5);
if(!strcmp("lock",argv[3]))
{
WRITE_LCKW(fd,0,SEEK_SET,0);
}
printf("lock success!\n");
printf("lock pid: %d\n",getpid());
//以单个字符写入文件
char *p=argv[1];
int i;
for(int i=0;i<size;i++)
{
if(write(fd,p+i,1)!=1)
{
perror("write error");
exit(1);
}
printf(" successfully write one character\n");
sleep(1);
}
//解锁
if(!strcmp("lock",argv[3]))
{
UNLOCK(fd,0,SEEK_SET,0);//解锁参数和 上锁参数一致
}
printf("unlock successfully\n");
printf("unlock pid:%d\n",getpid());
close(fd);
return 0;
}
注: WRITE_LCKW是我定义的一个宏:
//io.h文件内容:
extern int lock_reg(int fd, int cmd, short type,long int offset,short whence,long int length);
//宏定义:
//阻塞读锁
#define READ_LOCKW(fd, offset,whence,length) lock_reg(fd, F_SETLKW, F_RDLCK,offset , whence,length)
//非阻塞读锁
#define READ_LOCK(fd, offset,whence,length) lock_reg(fd, F_SETLK, F_RDLCK,offset , whence,length)
//阻塞写锁
#define WRITE_LCKW(fd, offset, whence,length) lock_reg(fd, F_SETLKW, F_WRLCK,offset, whence,length)
//非阻塞写锁
#define WRITE_LCK(fd, offset, whence,length) lock_reg(fd, F_SETLK, F_WRLCK,offset, whence,length)
//解锁
#define UNLOCK(fd, offset, whence, length) lock_reg(fd, F_SETLK, F_UNLCK,offset,whence, length)
//io.c的文件内容
int lock_reg(int fd, int cmd, short type,off_t offset,short whence,off_t length)
{
struct flock flock;
flock.l_type=type;
flock.l_start=offset;
flock.l_whence=whence;
flock.l_len=length;
//flock.l_pid=getpid();获得加锁的进程编号
if(fcntl(fd, cmd,&flock)<0)
{
perror("fcntl error");
return 0;//数字随便
}
return 1;//数字随便
}
结果:
执行:用脚本文件来自动开两个进程进行验证
结果