记录锁

UNIX高级编程指南(十三)之二

2000-06-13 00:00 作者: staw 出处: 日月光华站 责任编辑:
 
p align="JUSTIFY"> 12.3 记录锁(Record Locking)

  当两个人同时编辑一个文件时,其后果将如何呢?在很多Unix系统中,该文件最后状态取决于写该文件的最后一个进程。但是对于有些应用程序,例如数据库,有时进程需要确保它正在单独写一个文件。为了向进程提供这种能力,较新的Unix系统提供了记录锁机制。(在第十六章中包含了使用记录锁的数据库子程序库。)

  记录锁机制的功能是:一个进程正在读或修改文件的某个部分时,可以阻止其它进程修改同一文件区。对于Unix,记录这个定语也是误用,因为Unix系统核根本没有使用文件记录这种概念。一个更适合的术语可能是区域锁,因为它锁定的只是文件的一个区域(也可能是整个文件)。

  在本节的最后部分将说明建议性锁和强制性锁之间的区别。POSIX.1选择了以fcnt l函数为基础的系统V风格的记录锁。这种风格也得到4.3BSD Reno版本的支持。早期的贝克莱版只支持BSD flock函数。此函数只锁整个文件,而不锁文件中的一个区域。但是POSIX.1的fcntl函数可以锁文件中的任一区域,大至整个文件,小至单个字节。

  在本书中只说明POSIX.1的fcntl锁。系统V的lockf函数只是fcntl函数的一个界面。记录锁是1980年由John Bass最早加到Version7上的。系统核中相应系统调用入口项是名为locking的函数。此函数提供了强制性记录锁能,它传到了很多制造商的系统III版本。Xenix系统采用了此函数,SVR4在Xenix兼容库中仍旧支持该函数。

  SVR2是系统V中第一个支持fcntl风格记录锁的版本(1984)。

  fcntl记录锁

  3.13节中已经给出了fcntl函数的原型,为了叙说便,这里再重复一次。

#include
#include
#include
int fcnt1(int filedes,int cmd,…/* struct flock *flockptr */);

  返回:若成功依赖于cmd(见下)错为-1

  对于记录锁,cmd是F_GETLK、F_SETLK或F_SETLKW。第三个参数(我们将其称为flockptr)是一个指向flock结构的指针

Struct flock{
short l_type; /* F_RDLCK,F_WRLCK, 或 F_UNLCK */
off_t l_start; /*相对于l_w ce的字节位移量*/
short l_whence /SEEK_SET,SEEK_CUR,或SEEK_END */
off_t l_len; /*长度(字节),O表示锁至EOF */
pid_t l_pid; /*随F--FETLK命令返回
}

flock结构说明:

  所希望的锁类型:F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)、或F_UNLCK(解锁一个区域)

  要加锁或解锁的区域的起始地址,它由l_stant和l__whence两者决定。l_stat是相对位移量(字节),l_whence则决定了相对位移量的起。这与lseek中的使用方法一样。区域的长度,这由l_len表示。关于加锁和解锁区域的说明还要注意下列各点:

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

  如若l_len为0,则表示锁的区域从其起点(由l_start和l_whence决定)开始直至最大可能位置为止。也就是不管添写到该文件中多少数据,它都处于锁的范围。

  为了锁整个文件,通常的方法是将l_start说明为0,l_whence说明为SEEK_SET,l_len说明为0。

  上面提到了两个锁类型:共享读锁(l_type为F_RDLCK)和独占写琐(F_WRLCK)。基本规则是:多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上的写锁则只能由一个进程独用。更进一步而言,如果在一个给定加字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性的写锁,则不能再对它加任何读锁。在图12.2中示出了这些规则。

  为了加读锁,该描述必须是读打开,为了加写锁,该描述符必须是写打开。

  现在说明fcntl函数的三种命令。

  F_GETLK 决定由flockptr所说明的锁是否被另外一把锁所排斥(阻塞)。如果存在一把锁,它阻止创建由flockptr所描述符的锁,则这把现存的锁的信息写到flockptr指向的结构中;如果不存在这种情况,则除l_type设置为F_UNLCK之外,flockptr所指向结构中的其它信息、保持不变。

  F_SETLK 设置由flockptr所描述的锁。如果试图建立一把按上述兼容性规则并不允许的锁,则fcntl立即出错返回,此时errno设置为EACCES或EAGAIN。SVR2和SVR4返回EACCES,但手册页警告将来返回EAGAIN。4.3+BSD则返回EAGAIN。POSIX.1允许这两种情况。此命令也用来清除由flockptr说明的锁(l_type为F_UNLCK)。

  F_SETLKW 这是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。如果由于存在其它锁,那么按兼容性规则由flockptr所要求的锁不能被创建,则调用进程睡眠。如果捕捉信号则睡眠中断。

  应当了解,用F_GETLK测试能否建立一把锁,然后用F_SETLK和F_SETLKW企图建立一把锁,这两者不是一个原子作。在这两个操作之间可能会有另一个进程插入并建立一把相关的锁,使原来测试到的情况发生变化,如果不希望在建立锁时可能产生的长期阻塞,则应使用F_SETLK,并对返回结果进行测试,以判别是否成功地建立了所要求的锁。

  在设置或释放在一个文件上的一把锁时。系统按需组合或裂开相邻区。例如若100-199字节是加锁的区,然后解锁第150字节,则系统核将维持两把锁,一把是从100-149字节,另一把是从151-199字节。

实例-要求和释放一把锁

  为了免于每次分配flock结构,然后又填入各项信息,可以用程序12.2中的函数lock_reg来处理这些细节。

#include
#include
#include ourhdr.h
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len )
{
struct flock lock;
lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
lock.l_start = offset; /* byte offset, relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */
return( fcntl(fd, cmd, lock) );
}

  程序12.2 锁和解锁一个文件区域的函数

  因为大多数锁调用是锁或解锁一个文件区域(命令F_GETLK很少使用)。我们通常使用下列五个,它们都定义在ourhdr.h中(附录B)。

#define read_lock(fd,offset,whence,len)
lock_reg(fd,F_SETLK,F_RDLCK,offset,whence,len)
#define needw_lock(fd,offset,whence,len)
lock_reg(fd,F_SETLKW,F_RDLCK,offset,whence,len)
#define write_lock(fd,offset,whence,len)
lock_reg(fd,F_SETLK,F_WRLCK,offset,whence,len)
#define writew_lock(fd,offset,whence,len) lock_reg(fd,F_SETLKW,F_WRLCK,offset,whence,len)
#define un_lock(fd,offset,whence,len) lock_reg(fd,F_SETLK,F_UNLCK,offset,whence,len)

  我们以lseek函数中的同样顺序定义了这些宏中的三个参数。

实例-测试一把锁

  程序12.3定义了一个函数lock_test,可用其测试一把锁。

#include
#include
#include ourhdr.h
pid_t
lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;
lock.l_type = type; /* F_RDLCK or F_WRLCK */
lock.l_start = offset; /* byte offset, relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */
if (fcntl(fd, F_GETLK, lock) 0)
err_sys(fcntl error);
if (lock.l_type == F_UNLCK)
return(0); /* false, region is not locked by anotheproc */
return(lock.l_pid); /* true, return pid of lock owner */
}

  程序12.3 测试一个锁条件的函数

  如果存在一把锁,它阻塞由参数说明的锁,则此函数返回持有这把现存锁的进程的ID,否则此函数返回0。通常用下面两个宏来调用此函数(它们也定义在ourhdr.h )。

#define is_read_lockable(fd,offset,whence,len)
lock_test(fd,F_RDLCK,offset,whence,len)
#define is_write_lockable(fd,offset,whence,len)
lock_test(fd,F_WRLCK,offset,whence,len)

<script type="text/javascript"> var ad_cid; if (window.location.search.substring(1) != "") { ad_cid = window.location.search.substring(1); } else { ad_cid = 417; } </script> <script language="JavaScript" type="text/javascript"> document.getElementById("ad1").innerHTML=document.getElementById("span_ad1").innerHTML; document.getElementById("span_ad1").innerHTML=""; </script> <script type="text/javascript"> if(document.getElementById("contentAdv")) { document.getElementById("contentAdv").innerHTML=document.getElementById("span_ad3").innerHTML; document.getElementById("span_ad3").innerHTML=""; } else { document.getElementById("ad3").innerHTML=document.getElementById("span_ad3").innerHTML; document.getElementById("span_ad3").innerHTML=""; }</script> <script language="JavaScript" type="text/javascript"> document.getElementById("ad2").innerHTML=document.getElementById("span_ad2").innerHTML; document.getElementById("span_ad2").innerHTML=""; </script> <script language="JavaScript" type="text/javascript"> document.getElementById("ad10").innerHTML=document.getElementById("span_ad10").innerHTML; document.getElementById("span_ad10").innerHTML=""; </script> <script language="JavaScript" type="text/javascript"> document.getElementById("ad9").innerHTML=document.getElementById("span_ad9").innerHTML; document.getElementById("span_ad9").innerHTML=""; </script>
src="http://media.yesky.com/adjs/iframe-column/imp7.htm" frameborder="0" width="0" scrolling="no" height="0"> <script language="JavaScript1.1" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-chinabyte_js&dt=1170744511312&hl=zh-CN&gl=CN&adsafe=high&oe=gb&lmt=1170744511&num_ads=3&output=js&channel=soft&url=http%3A%2F%2Fwww.yesky.com%2F146%2F79646.shtml&ref=http%3A%2F%2Fwww.google.cn%2Fsearch%3Fcomplete%3D1%26hl%3Dzh-CN%26newwindow%3D1%26q%3Dflock%25E7%25BB%2593%25E6%259E%2584%26meta%3D&cc=100&u_h=768&u_w=1024&u_ah=738&u_aw=1024&u_cd=32&u_tz=480&u_java=true" type="text/javascript"></script>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值