有一次在看别人的代码的时候,发现代码中的一个类,在初始化的时候使用了flock()函数对文件进行了加锁操作,然后发现在关闭文件描述符的时候,并没有调用flock函数进行解锁,我就产生了疑问,到底flock能锁住文件吗?然后就自己写了一个小程序进行了一下测试:
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <iostream>
using namespace std;
class tflock{
public:
tflock(int flag):_flag(flag){
_fd = open("/root/003",O_CREAT | O_RDWR, 0666);
if(_fd>0){
//对文件进行加锁
int ret = flock(_fd,LOCK_EX |LOCK_NB);
cout<<"func = "<<_flag<<" flock ret = "<<ret<<endl;
}else{
cout<<"open failed"<<endl;
_fd = -1;
}
}
int getfd(){
return _fd;
}
~tflock(){
if(_fd > 0){
//析构函数对文件进行解锁
flock(_fd,LOCK_UN);
int clf = close(_fd);
if(clf)
{
cout<<"close fd faile clf = "<<clf<<endl;
}else{
cout<<"deconstructor successed and fd = "<<_fd<<" and flag = "<<_flag<<endl;}
}
}
private:
int _fd;//记录文件描述符
int _flag;//区分一下析构函数
};
简单写一个类,然后通过使用类来操作同一个文件
void* Test(void* para){
tflock tf(1);
int fd = tf.getfd();
if(fd>0){
write(fd,"helloworld",(size_t)10);
//因为析构函数执行解锁操作,该函数先执行,但是后析构,肯定会有写入冲突
sleep(5);
cout<<"Test fd = "<<fd<<" pid = "<<(long int)para<<endl;
}
return NULL;
}
void* Test2(void* para){
tflock tf(2);
int fd = tf.getfd();
if(fd>0){
write(fd,"12345678901112345678",(size_t)20);
cout<<"Test2 fd = "<<fd<<" pid = "<<(long int)para<<endl;
}
return NULL;
}
int main(){
pthread_t t1;
pthread_t t2;
pthread_create(&t1,NULL,Test,(void *)1);
//保证线程1先执行
sleep(1);
pthread_create(&t2,NULL,Test2,(void *)2);
pthread_join(t2,NULL);
sleep(1);
pthread_join(t1,NULL);
while(1);
return 0;
}
执行结果:
func = 1 flock ret = 0
func = 2 flock ret = -1 //第二个线程对文件加锁失败了,所以返回值是-1
Test2 fd = 4 pid = 2
deconstructor successed and fd = 4 and flag = 2//第二个函数先析构
Test fd = 3 pid = 1//fd不同,说明函数2重新打开文件,生成了一个新的文件描述符来对文件进行操作
deconstructor successed and fd = 3 and flag = 1
查看文件内容发现,函数1写入的内容被覆盖了,说明flock函数,没有对文件起到加锁的作用,其他线程依然可以对文件进行操作。
在其他文章中看到了关于flock的描述:
flock,建议性锁,不具备强制性。一个进程使用flock将文件锁住,另一个进程可以直接操作正在被锁的文件,修改文件中的数据,原因在于flock只是用于检测文件是否被加锁,针对文件已经被加锁,另一个进程写入数据的情况,内核不会阻止这个进程的写入操作,也就是建议性锁的内核处理策略。
因此flock函数实际上只是给了文件多一个状态,另外的线程可以检测这个加锁的状态,可以通过检测文件是否加锁,来进行自己的逻辑处理,真要防止写入冲突,还需要进行其他的加锁操作