MEMFD_CREATE - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-06-09
名称
memfd_create-创建一个匿名文件
语法
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <sys/mman.h> int memfd_create(const char *name, unsigned int flags);
说明
memfd_create()创建一个匿名文件并返回引用该文件的文件描述符。该文件的行为类似于常规文件,因此可以对其进行修改,截断,映射到内存等。但是,与常规文件不同,它位于RAM中,并具有易失性的后备存储。一旦删除了对该文件的所有引用,它将自动释放。匿名内存用于文件的所有后备页面。因此,由memfd_create()创建的文件与其他匿名内存分配(例如,使用带有MAP_ANONYMOUS标志的mmap(2)分配的那些匿名内存)具有相同的语义。
文件的初始大小设置为0。调用之后,应使用ftruncate(2)设置文件大小。 (或者,可以通过调用write(2)或类似内容填充文件。)
名称中提供的名称用作文件名,并将显示为目录/ proc / self / fd /中相应符号链接的目标。显示的名称始终以memfd:为前缀,并且仅用于调试目的。名称不会影响文件描述符的行为,因此,多个文件可以具有相同的名称,而不会产生任何副作用。
以下值可以按标志按位或以更改memfd_create()的行为:
- MFD_CLOEXEC
- 在新文件描述符上设置执行时关闭(FD_CLOEXEC)标志。有关为什么可能有用的原因,请参见open(2)中O_CLOEXEC标志的描述。
- MFD_ALLOW_SEALING
- 允许对此文件进行密封操作。请参阅下面的fcntl(2)中有关F_ADD_SEALS和F_GET_SEALS操作的讨论。最初的密封件是空的。如果未设置此标志,则初始印章集为F_SEAL_SEAL,这意味着无法在文件上设置其他印章。
- MFD_HUGETLB(since Linux 4.14)
- 匿名文件将使用巨大的页面在hugetlbfs文件系统中创建。请参阅Linux内核源文件Documentation / admin-guide / mm / hugetlbpage.rst,以获取有关hugetlbfs的更多信息。从Linux 4.16开始,支持在标志中同时指定MFD_HUGETLB和MFD_ALLOW_SEALING。
- MFD_HUGE_2MB, MFD_HUGE_1GB, ...
- 与MFD_HUGETLB结合使用,以在支持多个hugetlb页面大小的系统上选择其他的hugetlb页面大小(分别为2 MB,1 GB,...)。头文件中包含已知的巨大页面大小的定义。
- 有关对头文件中未包含的大页面大小进行编码的详细信息,请参见mmap(2)中有关类似命名的常量的讨论。
标志中未使用的位必须为0。
作为其返回值,memfd_create()返回一个新的文件描述符,该文件描述符可用于引用该文件。将打开该文件描述符以进行读写(O_RDWR),并为文件描述符设置O_LARGEFILE。
对于fork(2)和execve(2),通常的语义适用于由memfd_create()创建的文件描述符。文件描述符的副本由fork(2)生成的子代继承,并引用相同的文件。除非已设置close-on-exec标志,否则文件描述符将在execve(2)中保留。
返回值
成功后,memfd_create()返回一个新的文件描述符。发生错误时,将返回-1并将errno设置为指示错误。
错误说明
- EFAULT
- 名称中的地址指向无效的内存。
- EINVAL
- 标志包括未知位。
- EINVAL
- 名字太长。 (限制为249个字节,不包括终止的空字节。)
- EINVAL
- 在标志中同时指定了MFD_HUGETLB和MFD_ALLOW_SEALING。
- EMFILE
- 已达到打开文件描述符数量的每个进程限制。
- ENFILE
- 已达到系统范围内打开文件总数的限制。
- ENOMEM
- 没有足够的内存来创建新的匿名文件。
版本
memfd_create()系统调用首先出现在Linux 3.17中;在2.27版中添加了glibc支持。
遵循规范
memfd_create()系统调用是特定于Linux的。
备注
memfd_create()系统调用提供了一种简单的替代方法,可以手动安装tmpfs(5)文件系统并在该文件系统中创建和打开文件。 memfd_create()的主要目的是创建文件以及与fcntl(2)提供的文件密封API一起使用的文件描述符。
memfd_create()系统调用也具有不带文件密封的用途(这就是为什么文件密封被禁用的原因,除非使用MFD_ALLOW_SEALING标志明确请求)。特别是,它可以用作在tmp中创建文件的替代方法,或者在不打算将生成的文件实际链接到文件系统中的情况下用作使用open(2)O_TMPFILE的替代方法。
File sealing
在没有文件密封的情况下,通过共享内存进行通信的进程必须彼此信任,或者采取措施来处理不信任对等方可能以有问题的方式操纵共享内存区域的可能性。例如,不受信任的对等方可以随时修改共享内存的内容,或缩小共享内存区域。前一种可能性使本地进程易受使用时间检查竞争条件的影响(通常通过在检查和使用之前从共享内存区域复制数据来解决)。当试图访问共享内存区域中现在不存在的位置时,后一种可能性使本地进程容易受到SIGBUS信号的攻击。 (要解决这种可能性,必须为SIGBUS信号使用处理程序。)
与不受信任的对等对象打交道会在使用共享内存的代码上增加额外的复杂性。内存密封可以通过允许进程安全地运行,因为它知道对等端无法以不希望的方式修改共享内存,从而消除了额外的复杂性。
密封机构的用法示例如下:
- 1.
- 第一个过程使用memfd_create()创建一个tmpfs(5)文件。该调用将产生在后续步骤中使用的文件描述符。
- 2.
- 第一个过程使用ftruncate(2)对在上一步中创建的文件进行大小调整,使用mmap(2)对其进行映射,并使用所需数据填充共享内存。
- 3.
- 第一个过程使用fcntl(2)F_ADD_SEALS操作在文件上放置一个或多个密封,以便限制对文件的进一步修改。 (如果放置图章F_SEAL_WRITE,则必须首先取消映射在上一步中创建的共享可写映射。否则,可以通过使用F_SEAL_FUTURE_WRITE来实现类似于F_SEAL_WRITE的行为,这将防止将来通过mmap(2)进行写入和写入(2)保持现有共享可写映射成功。
- 4.
- A second process obtains a file descriptor for the
tmpfs(5)
file and maps it.
Among the possible ways in which this could happen are the following:- *
- 调用memfd_create()的进程可以通过UNIX域套接字将生成的文件描述符传输到第二个进程(请参见unix(7)和cmsg(3))。然后,第二个过程使用mmap(2)映射文件。
- *
- 第二个过程是通过fork(2)创建的,因此自动继承了文件描述符和映射。 (请注意,在本例和下例中,由于两个进程在相同的用户ID下运行,因此这两个进程之间存在自然的信任关系。因此,通常不需要文件密封。)
- *
- 第二个进程打开文件/ proc // fd /,其中是第一个进程的PID(称为memfd_create()的PID),并且是该进程中对memfd_create()的调用返回的文件描述符的编号。然后,第二个过程使用mmap(2)映射文件。
- 5.
- 第二个过程使用fcntl(2)F_GET_SEALS操作来检索已应用于文件的密封的位掩码。可以检查此位掩码,以确定对文件修改施加了哪些限制。如果需要,第二个过程可以应用进一步的密封以施加附加的限制(只要尚未应用F_SEAL_SEAL密封)。
示例
下面显示了两个示例程序,这些程序演示了memfd_create()和文件密封API的用法。
第一个程序t_memfd_create.c使用memfd_create()创建一个tmpfs(5)文件,设置该文件的大小,将其映射到内存中,并可选地在文件上放置一些图章。该程序最多接受三个命令行参数,其中前两个是必需的。第一个参数是与文件关联的名称,第二个参数是要为文件设置的大小,可选的第三个参数是指定要在文件上设置印章的字符串。
第二个程序t_get_seals.c可用于打开通过memfd_create()创建的现有文件,并检查已应用于该文件的一组密封。
以下shell会话演示了这些程序的用法。首先,我们创建一个tmpfs(5)文件并在其上设置一些密封:
$ ./t_memfd_create my_memfd_file 4096 sw & [1] 11775 PID: 11775; fd: 3; /proc/11775/fd/3
此时,t_memfd_create程序继续在后台运行。在另一个程序中,我们可以通过打开与由memfd_create()打开的文件描述符相对应的/ proc / [pid] / fd文件来获取由memfd_create()创建的文件的文件描述符。使用该路径名,我们检查/ proc / [pid] / fd符号链接的内容,并使用我们的t_get_seals程序查看已放置在文件上的印章:
$ readlink /proc/11775/fd/3 /memfd:my_memfd_file (deleted) $ ./t_get_seals /proc/11775/fd/3 Existing seals: WRITE SHRINK
Program source: t_memfd_create.c
#define _GNU_SOURCE #include <sys/mman.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { int fd; unsigned int seals; char *addr; char *name, *seals_arg; ssize_t len; if (argc < 3) { fprintf(stderr, "%s name size [seals]\n", argv[0]); fprintf(stderr, "\taqsealsaq can contain any of the " "following characters:\n"); fprintf(stderr, "\t\tg - F_SEAL_GROW\n"); fprintf(stderr, "\t\ts - F_SEAL_SHRINK\n"); fprintf(stderr, "\t\tw - F_SEAL_WRITE\n"); fprintf(stderr, "\t\tW - F_SEAL_FUTURE_WRITE\n"); fprintf(stderr, "\t\tS - F_SEAL_SEAL\n"); exit(EXIT_FAILURE); } name = argv[1]; len = atoi(argv[2]); seals_arg = argv[3]; /* Create an anonymous file in tmpfs; allow seals to be placed on the file */ fd = memfd_create(name, MFD_ALLOW_SEALING); if (fd == -1) errExit("memfd_create"); /* Size the file as specified on the command line */ if (ftruncate(fd, len) == -1) errExit("truncate"); printf("PID: %ld; fd: %d; /proc/%ld/fd/%d\n", (long) getpid(), fd, (long) getpid(), fd); /* Code to map the file and populate the mapping with data omitted */ /* If a aqsealsaq command-line argument was supplied, set some seals on the file */ if (seals_arg != NULL) { seals = 0; if (strchr(seals_arg, aqgaq) != NULL) seals |= F_SEAL_GROW; if (strchr(seals_arg, aqsaq) != NULL) seals |= F_SEAL_SHRINK; if (strchr(seals_arg, aqwaq) != NULL) seals |= F_SEAL_WRITE; if (strchr(seals_arg, aqWaq) != NULL) seals |= F_SEAL_FUTURE_WRITE; if (strchr(seals_arg, aqSaq) != NULL) seals |= F_SEAL_SEAL; if (fcntl(fd, F_ADD_SEALS, seals) == -1) errExit("fcntl"); } /* Keep running, so that the file created by memfd_create() continues to exist */ pause(); exit(EXIT_SUCCESS); }
Program source: t_get_seals.c
#define _GNU_SOURCE #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { int fd; unsigned int seals; if (argc != 2) { fprintf(stderr, "%s /proc/PID/fd/FD\n", argv[0]); exit(EXIT_FAILURE); } fd = open(argv[1], O_RDWR); if (fd == -1) errExit("open"); seals = fcntl(fd, F_GET_SEALS); if (seals == -1) errExit("fcntl"); printf("Existing seals:"); if (seals & F_SEAL_SEAL) printf(" SEAL"); if (seals & F_SEAL_GROW) printf(" GROW"); if (seals & F_SEAL_WRITE) printf(" WRITE"); if (seals & F_SEAL_FUTURE_WRITE) printf(" FUTURE_WRITE"); if (seals & F_SEAL_SHRINK) printf(" SHRINK"); printf("\n"); /* Code to map the file and access the contents of the resulting mapping omitted */ exit(EXIT_SUCCESS); }
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。