我有一种情况,我需要创建数千个 0 字节锁定文件以进行并发控制。
我已经使用以下方法测试了创建它们:
for i in `seq 1 50000`; do touch "/run/lock/${i}.lock"; done
由于文件是 0 字节,因此它们不会占用分区中的任何空间。看着df -h
:
Filesystem Size Used Avail Use% Mounted on
tmpfs 50M 344K 49M 1% /run
none 5.0M 0 5.0M 0% /run/lock
none 246M 0 246M 0% /run/shm
none 100M 0 100M 0% /run/user
该0%
数字在行中根本没有变化/run/lock
。
然而,每个锁文件的内存大小确实增加了大约 1KB。我通过比较free -h
在内部创建 70,000 个锁定文件之前和之后发现了这一点/run/lock
。这种内存增加反映在实际内存使用中(虚拟内存减去缓冲区/缓存)。
后来我发现这个 1KB 的增加很可能是由于 inode 造成的。所以我使用以下方法检查了 inode 使用情况df -i
:
Filesystem Inodes IUsed IFree IUse% Mounted on
tmpfs 62729 322 62407 1% /run
none 62729 50001 12728 80% /run/lock
none 62729 1 62728 1% /run/shm
none 62729 2 62727 1% /run/user
如您所见,锁定文件增加了/run/lock
分区内的 inode。
我目前在 Ubuntu 上,/run
安装并没有反映在/etc/fstab
. 跑步mount
给了我:
tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
none on /run/shm type tmpfs (rw,nosuid,nodev)
none on /run/user type tmpfs (rw,noexec,nosuid,nodev,size=104857600,mode=0755)
我对此有几个问题(但第一个是最重要的):
- 如何永久增加 inode 限制
/run/lock
?所以这个限制在重启后仍然存在? - 我创建自己的目录并在其上挂载 tmpfs 以用于此而不是使用会更好
/run/lock
吗? - 每个分区的大小限制是否完全相互独立?那是存储文件
/run
似乎没有影响/run/lock
,反之亦然。 - 1KB是从inode派生的吗?我注意到在创建非空文件时,每个文件的基本块是 4KB。
- 为什么
/run
给定文件系统类型tmpfs
but/run/lock
,给文件系统类型“none”/run/shm
,/run/user
特别是因为它们都由 TMPFS 支持?为什么他们不都像列tmpfs
中那样阅读Filesystem
? - 如果所有目录都受到独立约束,OOM 杀手如何处理存在多个完整 TMPFS 分区的情况,每个分区的大小为 RAM 的 50%,并且还有进程竞争 RAM。显然,不能使用超过 100% 的 RAM。根据https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt它提到系统将死锁。这是如何运作的?
回答您的一些问题,按顺序:
mount -o remount,nr_inodes=NUM /run/lock
在应用程序启动脚本中使用(以防它以 uid=0 运行)。将相关行添加到/etc/fstab也应该是安全的,但尚未测试。不确定您的应用程序是否通过打开它来创建空文件(以及持续多长时间),但您也可以考虑增加打开文件限制(检查ulimit)以避免耗尽。
你正朝着错误的方向前进。您可以使用文件系统语义来强制一致性。
当您想读取文件时,只需打开并读取它。您应该始终使用
open
, 永远不要access
进行此操作。如果您使用 PHP 库来执行此操作,请检查它是否只是调用open
而不是access
在文件上 - 但fopen
应该可以正常工作。当您要刷新或创建新文件时,请执行以下操作:-
这在操作上是安全的,因为重命名被定义为原子的。打开文件的阅读器将看到旧文件或新文件 - 但不会看到缓存中不存在的文件。
在对每个文件进行许多并发检查的最坏情况下,许多写入者将短暂地相互覆盖。但这是一种方式——比对每个文件使用文件锁要便宜得多。
或者,不要为每个文件都设置一个锁定文件 - 考虑实际上只是直接锁定每个单独的缓存对象。但是,我仍然认为这不会扩展。
在这种情况下使用
rename
和link
语义可以保证与缓存的一致性,并且比锁定文件更便宜。