当多个进程共同使用,操作一个文件的时候,可以通过采用给文件上锁的的方法,来避免共享的资源产生竞争的状态。
在linux中,实现文件上锁的函数有lock和fcntl。
fcntl可以对文件施加建议性锁,强制锁和记录锁。
1.建议锁又称协同锁。对于这种类型的锁,内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为。
2.强制锁,是OS内核的文件锁。每个对文件操作时,例如执行open、read、write等操作时,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