通过fcntl函数给文件上锁

当多个进程共同使用,操作一个文件的时候,可以通过采用给文件上锁的的方法,来避免共享的资源产生竞争的状态。

 

在linux中,实现文件上锁的函数有lock和fcntl。

 

fcntl可以对文件施加建议性锁,强制锁和记录锁。

 

 

1.建议锁又称协同锁。对于这种类型的锁,内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为。

2.强制锁,是OS内核的文件锁。每个对文件操作时,例如执行openreadwrite等操作时,OS内部检测该文件是否被加了强制锁,如果加锁导致这些文件操作失败。也就是内核强制应用程序来遵守游戏规则。

3.记录锁,即对文件的一部分进行加锁操作。记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁;而写入锁又称排斥锁,在任何时刻只能有一个进程在文件的某个部分上建上写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。

 

fcntl函数语法:

 #include

int fcntl(int fd,int cmd,struct flock * lock);  

fcntl()用来操作文件描述符的一些特性。参数fd代表欲设置的文件描述词,参数cmd代表欲操作的指令。  

有以下几种情况:  

F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述词,并且复制参数fd的文件描述词。执行成功则返回新复制的文件描述词。请参考dup2()。

F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。  

F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。  

F_GETFL 取得文件描述词状态旗标,此旗标为open()的参数flags。  

F_SETFL 设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。  

F_GETLK 取得文件锁定的状态。  

F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。  

F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。

 

参数lock指针为flock 结构指针,定义如下  

struct flcok  {  

short int l_type; 

short int l_whence;  

off_t l_start;  

off_t l_len;  

pid_t l_pid;  

}; 

l_type 有三种状态:  

F_RDLCK 建立一个供读取用的锁定  

F_WRLCK 建立一个供写入用的锁定  

F_UNLCK 删除之前建立的锁定  

 

l_whence 也有三种方式:  

SEEK_SET 以文件开头为锁定的起始位置。  

SEEK_CUR 以目前文件读写位置为锁定的起始位置  

SEEK_END 以文件结尾为锁定的起始位置。

 

返回值 成功则返回0,若有错误则返回-1,错误原因存于errno.

 

 

 

 

程序:

 

#include

#include

#include

#include

#include

#include

#include

 

/*

函数功能:给某个文件上锁

函数形参:文件描述符fd, 锁类型type

*/

void Lock_Set(int fd, int type)

{

struct flock lock; //锁结构体lock

lock.l_whence = SEEK_SET; //给lock赋值

lock.l_start = 0;

lock.l_len = 0;

 

while(1)

{

lock.l_type = type;

if( (fcntl(fd, F_SETLK, &lock)) == 0) //根据type类型给文件上锁或解锁

{

if(lock.l_type == F_RDLCK) //判断上锁类型

printf("read lock set by %d", getpid());

else if(lock.l_type == F_WRLCK)

printf("write lock set by %d", getpid());

else if(lock.l_type = F_UNLCK)

printf("release lock by %d", getpid());

 

return; //上锁成功后返回

 

fcntl(fd, F_GETLK, &lock); //获得文件锁定状态,赋值给lock.l_type

 

if(lock.l_type != F_UNLCK)  //若文件处于上锁状态

{

if(lock.l_type == F_RDLCK) //判断文件锁类型

printf("read lock already set by %d", lock.l_pid);

else if(lock.l_type == F_WRLCK) 

printf("write lock already set by %d", lock.l_pid);

 

getchar(); //等待输入

}

}

 

int main()

{

int fd;

 

struct flock lock; //锁结构体lock

lock.l_whence = SEEK_SET; //给lock赋值

lock.l_start = 0;

lock.l_len = 0;

 

fd = open("./dat", O_CREAT|O_RDWR, 0777); //打开文件dat

if(fd == -1)

{

perror("open failed/n");

exit(1);

}

 

 

 

Lock_Set(fd, F_WRLCK); //给文件上写入锁

getchar();

 

Lock_Set(fd, F_UNLCK); //给文件解锁

getchar();

 

close(fd);

 

return 0;

}

 

在linux中用两个终端运行观察结果如下:

终端1:

 

[root@localhost IO]# ./fcntl_write

write lock set by 4621

release lock by 4621

终端2:
[root@localhost IO]# ./fcntl_write
write lock already set by 4621
write lock already set by 4621
write lock set by 4645
release lock by 4645
可见,写入锁为排斥锁,任何时刻只能有一个。
若把main函数中

 

Lock_Set(fd, F_WRLCK); //给文件上写入锁
改为
Lock_Set(fd, F_RDLCK); //给文件上读取锁
其他不变。
运行结果如下:
终端1:
[root@localhost IO]# ./fcntl_read
read lock set by 4750
release lock by 4750
终端2:
[root@localhost IO]# ./fcntl_read
read lock set by 4751
release lock by 4751
可见,进程4750和进程4751同时对同一文件加上了读取锁。

 

你可能感兴趣的:(Linux应用编程)