为数十万个 0 字节锁文件设置 tmpfs `/run/lock`,并处理 inode 限制

为数十万个 0 字节锁文件设置 tmpfs `/run/lock`,并处理 inode 限制

我遇到一种情况,需要创建数十万个 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)

我对此有几个问题(但第一个是最重要的):

  1. 如何永久增加 inode 限制/run/lock?以便此限制在重启后仍然存在?
  2. 对于我来说,创建自己的目录并在其上挂载 tmpfs 来用于此目的而不是使用 是否更好/run/lock
  3. 每个分区的大小限制是否完全独立?也就是说,存储文件/run似乎不会产生影响/run/lock,反之亦然。
  4. 1KB 是从 inode 派生出来的吗?我注意到,在创建非空文件时,每个文件的基本块为 4KB。
  5. 为什么/run给出了文件系统类型,tmpfs但给出了文件系统类型“无”,尤其是因为它们都由 TMPFS 支持?为什么它们不都像列中那样读取?/run/lock/run/shm/run/usertmpfsFilesystem
  6. 如果所有目录都受到独立约束,那么 OOM 杀手如何处理存在多个完整 TMPFS 分区的情况,每个分区的大小为 RAM 的 50%,并且还有进程也在争用 RAM。显然,不能使用超过 100% 的 RAM。根据https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt它提到系统将死锁。这是怎么回事?

答案1

按以下顺序回答您的一些问题:

  1. 您可以在应用程序启动脚本中使用mount -o remount,nr_inodes=NUM /run/lock(如果使用 uid=0 运行)。将相关行添加到/etc/fstab,但尚未测试。
  2. 分离在这里有一定意义,因为填满所有 inode 不会干扰系统的其余部分。
  3. 是的,完全独立。
  4. [...]
  5. 使用虚拟(基于非块设备的)文件系统,您可以将任何内容作为设备放入 mount 命令中,只有类型才是最重要的。
  6. [...]

不确定您的应用程序是否会通过打开它来创建空文件(以及持续多长时间),但您也可以考虑增加打开文件的限制(检查限制),以免耗尽。

答案2

你走错了方向。你可以使用文件系统语义来强制一致性。

  1. 当你想读取一个文件时,只需打开并读取它。你应该总是此操作使用open,从不access。如果您使用 PHP 库来执行此操作,请检查它是否只调用open而不是access文件 - 但fopen应该可以正常工作。

  2. 当您想要刷新或创建新文件时,请执行以下操作:-

    • 使用临时文件创建机制创建新文件。如果不存在,则创建一个不太可能存在的新文件名(filename.XXXXXX,其中 X 被替换为随机字符)。确保在 O_EXCL 中打开。
    • 将相关数据写入文件。
    • 将文件重命名为旧文件的名称。

这种操作是安全的,因为重命名被定义为原子操作。打开文件的读者将看到旧文件或新文件 - 但永远不会看到缓存中不存在的文件。

在最坏的情况下,由于对每个文件进行多次并发检查,许多写入者会短暂地互相覆盖。但这比对每个文件使用文件锁便宜得多。

或者,与其为每个文件设置一个锁定文件,不如考虑直接锁定每个单独的缓存对象。不过,我仍然认为这无法扩展。

在这种情况下使用renamelink语义可以保证与缓存的一致性,并且比锁定文件管理起来便宜得多。

相关内容