- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在尝试在 python 3x 和 linux/macOS 中实现一个“记录管理器”类。该类(class)相对简单明了,我唯一想要的“困难”是能够在多个进程上访问同一个文件(保存结果的地方)。
从概念上讲,这似乎很简单:保存时,获取文件的独占锁。更新您的信息,保存新信息,释放文件的独占锁。很简单。
我正在使用 fcntl.lockf(file, fcntl.LOCK_EX)
获取独占锁。问题是,在互联网上,我发现 很多 不同的网站都在说这不可靠,它不能在 Windows 上运行,对 NFS 的支持不稳定,并且macOS 和 linux 之间可能会发生变化。
我已经接受该代码无法在 Windows 上运行,但我希望能够使其在 macOS(单机)和 Linux(在具有 NFS 的多台服务器上)上运行。
问题是我似乎无法完成这项工作;经过一段时间的调试并在 macOS 上通过测试后,一旦我在带有 linux (ubuntu 16.04) 的 NFS 上尝试它们,它们就失败了。问题是多个进程保存的信息不一致 - 一些进程缺少修改,这意味着锁定和保存过程出了问题。
我确定我做错了某事,我怀疑这可能与我在网上阅读的问题有关。那么,通过 NFS 处理对在 macOS 和 linux 上运行的同一文件的多次访问的正确方法是什么?
编辑
这是将新信息写入磁盘的典型方法:
sf = open(self._save_file_path, 'rb+')
try:
fcntl.lockf(sf, fcntl.LOCK_EX) # acquire an exclusive lock - only one writer
self._raw_update(sf) #updates the records from file (other processes may have modified it)
self._saved_records[name] = new_info
self._raw_save() #does not check for locks (but does *not* release the lock on self._save_file_path)
finally:
sf.flush()
os.fsync(sf.fileno()) #forcing the OS to write to disk
sf.close() #release the lock and close
虽然这是只从磁盘读取信息的典型方法看起来像这样:
sf = open(self._save_file_path, 'rb')
try:
fcntl.lockf(sf, fcntl.LOCK_SH) # acquire shared lock - multiple writers
self._raw_update(sf) #updates the records from file (other processes may have modified it)
return self._saved_records
finally:
sf.close() #release the lock and close
此外,这就是 _raw_save 的样子:
def _raw_save(self):
#write to temp file first to avoid accidental corruption of information.
#os.replace is guaranteed to be an atomic operation in POSIX
with open('temp_file', 'wb') as p:
p.write(self._saved_records)
os.replace('temp_file', self._save_file_path) #pretty sure this does not release the lock
错误信息
我编写了一个单元测试,我在其中创建了 100 个不同的进程,其中 50 个读取同一个文件,50 个写入同一个文件。每个进程都会做一些随机等待以避免顺序访问文件。
问题是有些记录没有保留;最后有 3-4 条随机记录丢失,所以我最终只得到 46-47 条记录,而不是 50 条。
编辑2
我修改了上面的代码,我获得的不是文件本身的锁,而是一个单独的锁文件。这防止了关闭文件会释放锁的问题(如@janneb 所建议的那样),并使代码在 mac 上正常工作。但是,相同的代码在使用 NFS 的 Linux 上失败了。
最佳答案
我不明白文件锁和 os.replace() 的组合有何意义。当文件被替换(也就是目录项被替换)时,所有已经存在的文件锁(可能包括等待加锁成功的文件锁,这里的语义不太清楚)和文件描述符都会对旧文件,不是新文件。我怀疑这是导致您在测试中丢失一些记录的竞争条件背后的原因。
os.replace() 是一种很好的技术,可以确保读者不会阅读部分更新。但它在面对多个更新程序时并不能很好地工作(除非丢失一些更新是可以的)。
另一个问题是 fcntl 是一个非常非常愚蠢的 API。特别是,锁绑定(bind)到进程,而不是文件描述符。这意味着例如指向文件的任何文件描述符上的 close() 将释放锁定。
一种方法是使用“锁定文件”,例如利用 link() 的原子性。来自 http://man7.org/linux/man-pages/man2/open.2.html :
Portable programs that want to perform atomic file locking using a lockfile, and need to avoid reliance on NFS support for O_EXCL, can create a unique file on the same filesystem (e.g., incorporating hostname and PID), and use link(2) to make a link to the lockfile. If link(2) returns 0, the lock is successful. Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.
如果可以读取稍微陈旧的数据,那么您可以仅对更新文件时使用的临时文件使用此 link() 舞蹈,然后使用 os.replace() 用于读取的“主”文件(阅读然后可以是无锁的)。如果不是,那么您需要为“主”文件执行 link() 技巧,而忘记共享/独占锁定,所有锁都是独占的。
附录:使用锁定文件时要处理的一件棘手的事情是当进程由于某种原因终止并留下锁定文件时该怎么办。如果这是在无人值守的情况下运行,您可能需要合并某种超时和删除锁定文件(例如检查 stat() 时间戳)。
关于python - 如何在 NFS 上进行正确的文件锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48770531/
我正在运行 Debian GNU/Linux 7 VM mount.nfs 版本 mount.nfs: (linux nfs-utils 1.2.6) 我想在 NFS 挂载上设置配额。 NFS 服务器
我正在尝试使用 VirtualBox 作为提供程序并使用 ansible 作为配置工具来“升级”一台 CentOS 机器 (centos7-x64-vbox43)。我执行了以下命令: vagrant
我最近使用 inotify 创建了一个保管箱系统,监视在特定目录中创建的文件。我正在监视的目录是从 NFS 服务器挂载的,并且 inotify 的行为与我的预期不同。考虑以下场景,其中 inotify
我已经在我的本地机器上安装了一个 NFS 挂载(所以,我想我的机器是 NFS 客户端)并且一个文件正在被托管 NFS 挂载的远程机器(NFS 服务器)写入 NFS 挂载中。现在,我如何使用 JAVA
我关注这个tutorial为我的服务器设置 NFS。目前,我有 1 台服务器将其部分目录共享为 HOST,2 台服务器将这些目录作为 CLIENT。 我的问题很直接,如果我的HOST包含200万张图片
我在主机上有一个 NFS 分区,如果将其添加到容器中 docker run -i -t -v /srv/nfs4/dir:/mnt ubuntu /mnt 将包含共享数据,但它不会导致冲突吗?因为它还
没有 iptables 规则,我可以挂载我的 NFSSERVER:/PATH 但有了它(firewall/iptables) 已启用,但我无法安装。 [.e.g., after iptables --
我们有两个数据中心,每个数据中心都有许多共享一个基于 EMC 的大型 nfs 的 Linux 服务器。 挑战在于保持两个 nfs 同步。目前假设写入仅发生在 nfs1 上,然后 nfs1 必须将更改传
我在尝试挂载 nfs 导出时遇到以下错误。 sudo mount 192.168.1.175:/mnt/nas /mnt/c/nas mount.nfs: No such device 关于如何解
NFS 挂载在我的 RHEL 7 AWS 实例中不起作用。 当我做一个 mount -o nfsvers=3 10.10.11.10:/ndvp2 /root/mountme2/ 我得到错误: mou
我正在复制 Controller 示例 [1] 中尝试 Kubernetes NFS 卷声明。 我已经设置了 NFS 服务器、PV 和 PVC。我的复制 Controller 看起来像这样 apiVe
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我想查找并更改用户帐户及其在本地文件系统上的默认组,但不包括大量 NFS 安装和指向这些安装的符号链接(symbolic link)。我尝试了多种语法但没有成功 - find / -user dumm
我正在使用 docker NFS 容器,但是在安装导出的目录(即使在本地主机上)时遇到了麻烦。 问题 exportfs: does not support NFS export 设置 我的容器使用入
HDFS NFS GateWay mount.nfs:输入/输出错误? 1.报错如下: [root@xx sbin]# mount -t nfs -o vers=3,proto=tcp,nolock,
我无法为 Docker Swarm 安装 NFS 卷,并且缺乏有关 --mount 语法( https://docs.docker.com/engine/reference/commandline/s
我有一个 Kubernetes 集群设置(本地),它有一个 NFS 共享(my-nfs.internal.tld)安装到 /exports/backup在每个节点上创建备份。 现在我正在设置我的日志记
我想将 sqlite 数据库嵌入到现有的 tcl 应用程序中(从平面文件迁移)。 目前;我们的 tcl 解释器是从网络位置运行的; /bin/tclsh8.3 我有一个 nfs $PATH对于已经为所
我在一台服务器上为客户端创建了一个 NFS 共享服务器和客户端都是centos 6系统。服务器 nfs 导出:/srv/diskless/tmp *(rw,sync,no_root_squash)。
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 10 个月前关闭。 Improve
我是一名优秀的程序员,十分优秀!