Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30001188
  • 博文数量: 2065
  • 博客积分: 10377
  • 博客等级: 上将
  • 技术积分: 21525
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-04 17:50
文章分类

全部博文(2065)

文章存档

2012年(2)

2011年(19)

2010年(1160)

2009年(969)

2008年(153)

分类: 数据库开发技术

2010-06-16 22:20:05

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

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

在本节的最后部分将说明建议性锁和强制性锁之间的区别。POSIX.1选择了以fcntl函数为基础的系统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记录锁 (哦怪不得了。在PYTHON里面我们看到了一个这样的函数原来如此!)

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)
阅读(1060) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~