有没有没有文件名的 shm_open() 之类的东西?

Is there anything like shm_open() without filename?

POSIX shm_open() 函数 returns 一个可用于访问共享内存的文件描述符。这非常方便,因为可以使用所有控制文件描述符的传统机制来控制共享内存。

唯一的缺点是 shm_open() 总是需要一个文件名。所以我需要这样做:

// Open with a clever temp file name and hope for the best.
fd = shm_open(tempfilename, O_RDWR | O_CREAT | O_EXCL, 0600);

// Immediately delete the temp file to keep the shm namespace clean.
shm_unlink(tempfilename);

// Then keep using fd -- the shm object remains as long as there are open fds.

tempfilename 的这种使用很难移植和可靠地进行。文件名的解释(命名空间是什么,如何处理权限)因系统而异。

在许多情况下,使用共享内存对象的进程不需要文件名,因为只需将文件描述符从一个进程传递到另一个进程,就可以更简单、更安全地访问该对象。那么有没有类似于 shm_open() 但可以在不触及共享内存文件名命名空间的情况下使用的东西?

mmap()MAP_ANON|MAP_SHARED 很棒,但它提供的不是文件描述符,而是一个指针。指针不能在 exec 边界上存活,也不能像文件描述符那样通过 Unix 域套接字发送到另一个进程。

默认情况下,shm_open() 返回的文件描述符也不会在 exec 边界存在:POSIX 定义表明 FD_CLOEXEC 文件描述符标志关联 但可以在 MacOS、Linux、FreeBSD、OpenBSD、NetBSD、DragonFlyBSD 和其他操作系统上使用 fcntl() 清除标志。

不,没有。由于 System V 共享内存模型和 IPC 的 POSIX 共享文件映射都需要对文件进行操作,因此始终需要文件才能进行映射。

mmap() with MAP_ANON|MAP_SHARED is great but instead of a file descriptor it gives a pointer. The pointer doesn't survive over an exec boundary and can't be sent to another process over a Unix domain socket like file descriptors can.

正如John Bollinger所说,

Neither memory mappings created via mmap() nor POSIX shared-memory segments obtained via shm_open() nor System V shared-memory segments obtained via shmat() are preserved across an exec.

内存中一定有一个well-known地方可以见面交流信息。这就是为什么需要文件的原因。通过这样做,在 exec 之后,child 能够重新连接到适当的共享内存。

This use of tempfilename is difficult to do portably and reliably. The interpretation of the filename (what the namespace is, how permissions are handled) differs among systems.

您可以让 mkstemp/dev/shm//tmp 中创建一个唯一的文件名并为您打开该文件。然后您可以 unlink 文件名,这样除了具有从 mkstemp.

返回的文件描述符的进程外,没有其他进程可以打开该文件

mkstemp(): CONFORMING TO 4.3BSD, POSIX.1-2001.

一个解决问题的库

我设法编写了提供简单界面的 a library

int shm_open_anon(void);

库在没有警告的情况下编译,并在 Linux、Solaris、MacOS、FreeBSD、OpenBSD、NetBSD、DragonFlyBSD 和 Haiku 上成功运行测试程序。您也许可以将其适应其他操作系统;如果你这样做,请发送拉取请求。

库 returns 设置了 close-on-exec 标志的文件描述符。您可以在所有支持的操作系统上使用 fcntl() 清除该标志,这将允许您通过 exec() 传递 fd。测试程序证明这是可行的。

库中使用的实现技术

库的自述文件对每个 OS 已完成和未完成的内容进行了非常精确的注释。这是主要内容的摘要。

有几个不可移植的东西或多或少等同于没有文件名的shm_open()

  • 自 2008 年起,FreeBSD 可以将 SHM_ANON 作为 shm_open() 的路径名。

  • Linux 从内核版本 3.17 开始有一个 memfd_create() 系统调用。

  • Linux 的早期版本可以使用 mkostemp(name, O_CLOEXEC | O_TMPFILE),其中 name 类似于 /dev/shm/XXXXXX。请注意,我们在这里根本没有使用 shm_open() —— mkostemp() 隐含地使用了一个非常普通的 open() 调用。 Linux 在 /dev/shm 中安装了一个特殊的内存支持文件系统,但一些发行版使用 /run/shm 代替,所以这里有陷阱。而且您仍然需要 shm_unlink() 临时文件。

  • OpenBSD 自 5.4 版以来有一个 shm_mkstemp() 调用。您仍然需要 shm_unlink() 临时文件,但至少它很容易安全地创建。

对于其他 OSes,我做了以下操作:

  1. 找出 POSIX shm_open()name 参数的 OS 依赖格式。请注意,没有您可以传递的绝对可移植的名称。例如,NetBSD 和 DragonFlyBSD 对名称中的斜杠有冲突的要求。即使您的目标是使用命名的 shm 对象(为此设计了 POSIX API)而不是匿名对象(正如我们在这里所做的那样),这也适用。

  2. 在名称中附加一些随机字母和数字(通过阅读 /dev/random)。这基本上就是 mktemp() 所做的,只是我们不检查我们的随机名称是否存在于文件系统中。 name 参数的解释千差万别,因此没有合理的方法将其可移植地映射到实际文件名。此外,Solaris 并不总是提供 mktemp()。出于所有实际目的,我们输入的随机性将确保我们需要的那几分之一秒内的唯一名称。

  3. 通过 shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600) 打开具有该名称的 shm 对象。在我们的随机文件名已经存在的天文机会中,O_EXCL 无论如何都会导致此调用失败,所以没有坏处。 0600 权限(所有者读写)在某些系统上是必需的,而不是空白的 0 权限。

  4. 立即调用shm_unlink()去掉乱名。文件描述符保留供我们使用。

POSIX 不保证此技术有效,但是:

  1. shm_open() name 参数未被 POSIX 指定,因此不能保证其他任何东西都有效。
  2. 我会让上面的兼容性列表说明一切。

尽情享受吧。

为什么不用 0 的访问权限创建它? 因此,没有进程能够 "open" 它并让您在之后安全地取消链接?