Bash脚本编写: 读写锁

3
想象一下一个由几个nix机器组成的网络。 独立的节点存储文件,并定期安排 任务A 来修改这些文件。 其他每个节点都会安排任务B,将(使用rsync )这些文件同步到本地存储。
由于任务A可能需要相当长的时间,而且所有节点上的文件集合需要保持一致状态,因此在任务A运行时不应运行任务B
实现此锁定机制的一种可能解决方案是使用读写锁。 任务A任务B将分别在资源上放置写锁和读锁。
我想知道如何在unix shell脚本中实现这样的锁定机制。
1个回答

2
通常的做法是使用flock实用工具,它是util-linux软件包的一部分。FreeBSD和NetBSD软件包也可用,而且可能还有其他的。(对于MacOSX,请参见这个问题。) flock命令可以进行读取(“共享”)锁定和写入(“独占”)锁定。它基于flock(2)系统调用,并因此是协作式锁定(也称为咨询式锁定),但在大多数应用程序中,这将很好地工作(但请参见下面关于文件远程的情况)。
链接的手册页中有使用示例。最简单的用法情况是:
flock /tmp/lockfile /usr/local/bin/do_the_update
flock /tmp/lockfile -s /usr/local/bin/do_the_rsync

两个命令都获取了 /tmp/lockfile 的锁,然后执行指定的命令(可能是一个 shell 脚本)。第一个命令获取了一个独占锁;我可以使用 -x 选项明确表示。第二个命令获取了一个共享锁。
由于这个问题实际上涉及到网络锁的需求,因此需要指出,flock()在网络文件系统上可能不可靠。通常,目标文件应该始终是本地的。
即使在非分布式应用程序中,您也需要考虑失败的可能性。例如,假设您正在本地进行rsync同步以创建副本。如果主机在rsync处理过程中崩溃,您将得到一个不完整或损坏的副本。rsync可以从中恢复,但不能确定当主机重新启动时,rsync是否在文件被修改之前启动。这不应该是一个问题,但您肯定需要考虑它。
在分布式应用程序中,情况更加复杂,因为整个系统很少失败。不同服务器或网络本身可能会独立发生故障。
建议锁定不是持久性的。如果锁文件所在的主机崩溃并保持锁定状态,则在重启后锁将不再保持。另一方面,如果持有锁的远程服务器之一崩溃并重新启动,则可能不知道自己持有锁,这种情况下锁将永远不会被释放。
如果两个服务器都能完全了解彼此的状态,这个问题就不会存在,但很难区分网络故障和主机故障。您需要评估风险。与本地情况一样,如果文件服务器在rsync进行时崩溃,它可能会重新启动并立即开始修改文件。如果远程rsync在文件服务器宕机期间没有失败,则它们将继续尝试同步,导致复制品损坏。使用rsync,这应该会在下一个同步周期中解决,但在此期间您会遇到问题。您需要决定这有多严重。您可以通过使用持久锁来防止文件服务器在启动时启动突变体。每个rsync服务器在启动rsync之前在主机上创建自己的锁文件(并且在已知文件存在之前不启动rsync),并在释放读锁之前删除该文件。如果一个rsync服务器重新启动并且其指示器文件存在,则它知道在rysnc期间发生了崩溃,因此必须删除指示器文件并重新启动rsync。
这通常是可行的,但如果rsync服务器在同步期间崩溃并且从未重新启动,或者仅在很长时间后重新启动,它可能会失败。(或者等效地,如果网络故障将rsync服务器隔离了很长时间。)在这些情况下,可能需要手动干预。在文件服务器上运行一个看门狗进程会很有用,如果读锁被持有的时间太长,就会向操作员发出警报,对于“太长”的定义有所不同。

我进行了一些额外的研究,发现远程使用文件锁并不像本地使用那样简单。结合本地和远程锁可能会更加复杂。您知道在这个领域有什么最佳实践吗? - tamasf
@tamasf:是的,我应该提到这一点。我会编辑答案。远程锁定没有完美的解决方案,但有替代方案。 - rici

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接