记录锁

记录锁的功能是:当一个进程正在读或者修改文件的某一个部分时,它可以阻止其他进程修改同一文件区。记录锁其实是

字节范围锁,因为它锁定的只是文件中的一个区域,也可能是整个文件。

1.fcntl记录锁

SVR3通过fcntl函数增加了记录锁功能。fcntl函数的原型已经在以前给出,这边再重复一次。

#include<fcntl.h>

int fcntl(int filedes, int cmd, .../* struct flock *flockptr */);

//成功则依赖于cmd,出错则返回-1.

对于记录锁,cmd是F_GETLK、F_SETLK或F_SETLKW。第三个参数是一个指向flock结构的指针。

struct flock{

short l_type; //F_RDLCK,F_WRLCK, or F_UNLCK

off_t l_start; //offset in bytes, relative to l_whence

short l_whence; //SEEK_SET, SEEK_CUR or SEEK_END

off_t l_len; //length in bytes; 0 means lock to EOF

pid_t l_pid; //returned with F_GETLK

};

关于加锁解锁区域注意以下几点:

 

1.l_start是相对偏移量,l_whence决定了相对偏移量的起点。这与lseek函数中最后两个参数类似。

2.该区域可以在当前文件尾端处开始或越过其尾端处开始,但是不能在文件起始位置之前开始。

3.如若l_len为0,则表示锁的区域从其起点(由l_start和l_whence决定)开始直至最大可能偏移量为止。(也就是不管

添加到该文件中多少数据,它们都处于锁的范围内)

4.为了锁整个文件,我们设置l_start和l_whence,使锁的起点在文件起始处,并且说明l_en为0.

 

上面说到的读写锁和线程互斥中的读写锁原理差不多:

 

以下说明fcntl函数的三种命令:

F_GETLK:判断由flockptr所描述的锁是否会被另外一把锁所排斥。如果存在一把锁,它阻止创建由flockptr所描述的锁,

则把该现存锁的信息写到flockptr指向的结构中,如果不存在这中情况,则除了将l_type设置为UNLCK之外,flockptr所指向

结构的其他信息保持不变。

F_SETLK:设置由flockptr所描述的锁,如果试图建立一把锁(读锁或者写锁),而按上述兼容性规则不能允许,则fcntl立即

出错返回,此时errno设置为EACCES或EAGAIN。此命令也用来消除由flockptr说明的锁(l_type为UNLOCK)

F_SETLKW:这是F_SETLK的阻塞版本,如果因为当前在所请求区间的某个部分另一个进程已经有一把锁,因而按兼容性

规则由flockptr所请求的锁不能被创建,则使调用进程休眠,如果请求创建的锁已经可用或者休眠由信号终端,则该进程被唤醒。

 

在设置或释放文件上的锁时,系统按照要求组合或裂开相邻区,例如,若字节100~199是加锁区,需解锁第150字节,则内核

将维持两把锁,一把用于字节100~149,另一把用于字节151~199.

 

假定我们又对第150字节设置锁,那么系统将会把三个相邻的加锁区合并成一个区,其结果如上图的第一图所示。

 

实践:

lock.c

#include <stdio.h>

#include <fcntl.h>


int main(void){

struct flock lock;

int fd;

if((fd = open("lock.txt",O_RDWR))<0){

perror("open");

return -1;

}


//printf("fd:%d\n",fd);

printf("pid:%d\n",getpid());


lock.l_type=F_WRLCK;

lock.l_start = 4;

lock.l_whence = SEEK_SET;

lock.l_len = 1;


if(fcntl(fd,F_SETLK, &lock) < 0){

perror("fcntl");

return -1;

}


while(1){

sleep(1);

}

return 0;

}


chlok.c



#include <stdio.h>

#include <fcntl.h>


int main(void){

struct flock lock;

int fd;

if((fd = open("lock.txt",O_RDWR))<0){

perror("open");

return -1;

}

//printf("fd:%d\n",fd);


lock.l_type=F_WRLCK;

lock.l_start = 3;

lock.l_whence = SEEK_SET;

lock.l_len = 2;


if(fcntl(fd,F_GETLK, &lock) < 0){

perror("fcntl");

}


if(lock.l_type == F_UNLCK){

printf("not locked.\n");

}else{

printf("lock.\n");

printf("lock.l_type:%s.\n",(lock.l_type == F_WRLCK)?"F_WRLCK":"not F_WRLCK");

printf("lock.l_start:%ld.\n",lock.l_start);

printf("lock.l_whence:%s.\n",(lock.l_whence == SEEK_SET)?"SEEK_SET":"not SEEK_SET");

printf("lock.l_len:%ld.\n",lock.l_len);

printf("lock.l_pid:%d.\n",lock.l_pid);

}


return 0;

}

 

root@gmdz-virtual-machine:~# cat lock.txt
1234567890

 

先运行lock,再运行chlock。

 

root@gmdz-virtual-machine:~# ./lock
pid:4181

 

root@gmdz-virtual-machine:~# ./chlock
lock.
lock.l_type:F_WRLCK.
lock.l_start:4.
lock.l_whence:SEEK_SET.
lock.l_len:1.
lock.l_pid:4181.

 

因为进程4181在锁定了字节5,所以chlock在锁定字节4,5时出错,flock中记录当前锁的信息。

 

2.锁的隐含继承和释放

关于记录锁的自动继承和释放有三条规则:

1.第一,当一个进程终止时,它所建立的锁全部释放。第二,任何时候关闭一个描述符时,则该进程通过这一描述符可以引用

的文件上的任何一把锁都被释放,这意味着如果执行下列四步:

 fd1 = open(pathname, ...);

read_lock(fd1,...);

fd2 = dup(fd1);

close(fd2);

则在close(fd2)后,在fd1上设置的锁被释放。如果将dup换成open,以打开另一描述符上的同一文件,其效果也是一样的。

2.由fork产生的子进程不继承父进程所设置的锁。

3.在执行exec后,新程序可以继承原执行程序的锁。但是,如果对一个文件描述符设置了close-on-exec标志,那么当作为exec

的一部分关闭该文件描述符时,对相应文件的所有锁都被释放了。

 

3.建议性锁和强制性锁

1.建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上

都坚持不使用建议性锁,它们依靠程序员遵守这个规定。(Linux默认是采用建议性锁)

2.强制性锁是由内核执行的。当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该

文件的读或写访问,每次读或写访问都得检查锁是否存在。

 本文来自 TODD911 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/todd911/article/details/18654693?utm_source=copy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值