下一个: , 上一个: CVS in repository, 上层: Repository storage


2.2.6 仓库中的 CVS 锁

关于 cvs 锁用于用户可见特性方面的介绍,请参看 Concurrency。 接下来的内容是针对那些想制作工具软件的朋友,这些软件可以访问 cvs 仓库,但不会与其它访问同一库的工具冲突。 如果你发现你被这里的一些名词,如“读锁(read lock)”、“写锁(write lock)”和“死锁(deadlock)”,弄得混淆不清,建议你可以去阅读一些关于操作系统和数据库的资料。

在仓库中任何一个以 #cvs.rfl. 开头的文件是有个读锁。 以 #cvs.pfl 开头的文件就是个可升级读锁。 以 #cvs.wfl 开头的文件是个写锁。 老版本的 cvs(在 cvs1.5 之前的版本)也会创建一些以 #cvs.tfl 开头的文件,在这里我们不讨论它们。 目录 #cvs.lock 的作用是一个主锁(master lock)。 它的意思是在创建其它种类锁之前你必须获得这个主锁。

为了得到一个读锁,首先创建 #cvs.lock 目录。 这样的一个操作必须是一个原子操作(在大多数操作系统下面创建目录都是可以的)。 如果失败,是因为这个目录已经存在,这个操作会等待一会再重新执行。 在得到 #cvs.lock 之后,会创建一个文件,以 #cvs.rfl. 开头,后面为你选择的信息(例如主机名和进程识别号)。 接着删除 #cvs.lock 目录以释放主锁。 接下来开始读仓库。 读操作完成后删除 #cvs.rfl. 文件以释放读锁。

可升级读锁是在其他并行论述中没有提及的概念。 它们允许两个(或多个)在第一次(读)时对文件锁设为读的文件认证,然后在最后认证必要的时候将读锁升级为写锁,并仍然假设自此首次读时没有改变。 例如,cvs 使用可升级读锁来防止提交和打标签认证时读处理的相互干扰。 它只可以在写认证时锁住一个单独的目录。

为了获得一个可升级读锁,首先创建 #cvs.lock 目录,就像普通读锁一样。 然后检查那里是否有以 #cvs.pfl 开头的文件。 如果存在,删除 #cvs.lock 主目录,等一会儿(CVS 在锁之间会等 30 秒)再试。 如果不存在其他的可升级锁,就创建一个 #cvs.pfl 名字以开头的文件,后面跟着你自己定义的信息(比如,CVS 是使用创建锁的 CVS 服务器进程所在的主机名和进程 id 号)。 如果版本低于 1.12.4 的 cvs 直接访问你的仓库(没有通过 1.12.4 或更新版本的 cvs 服务器),你应该创建一个读锁,因为旧版本的 CVS 会忽略可升级读锁创建自己的写锁。 接着删除 #cvs.lock 主目录以使其它进程可以获得读锁。

为了得到一个写锁,同读锁一样首先创建 #cvs.lock 目录。 接下来检查是否不存在以 #cvs.rfl.#cvs.pfl 开头的文件,这些是不属于试图获取写锁的进程。 如果存在就删除 #cvs.lock 目录,等待一会再重试。 如果没有其他进程的读或可升级读锁,就创建一个文件,以 #cvs.wfl 开头,后面为你选择的信息(同样,CVS 使用主机名和服务器进程识别号)。 删除你自己的 #cvs.pfl 文件。 继续保持住 #cvs.lock 锁。 开始进行写仓库操作。 当操作结束,首先删除 #cvs.wfl 文件,接着删除 #cvs.lock 目录。 需要说明的是不像 #cvs.rfl 文件,#cvs.wfl 文件仅仅只有提示作用;不能锁住操作,而这个功能是由 #cvs.lock 来完成的。

注意,每一个锁(读锁或者写锁)仅仅只锁住仓库中的单一目录, 包括 Attic 目录和 CVS 目录,但是不包括在版本控制中代表其它目录的子目录。 如果要锁住整个目录树,你必须锁住每一个目录(如果你在锁任何一个目录时出错,为了避免死锁就必须在重试之前释放整个目录树)。

还需注意的是 cvs 希望用写锁来控制任何 foo,v 文件的访问。 rcs 有一个计划,让 ,foo, 文件具有锁的作用,但 cvs 并没有这样实现,而且建议使用 cvs 的写锁。 关于此更多的讨论和基本原理的信息请参看 cvs 源码中的 rcs_internal_lockfile 注释。