100. memfd_create - 创建匿名内存文件描述符 見出しへのリンク
1. 函数介绍 見出しへのリンク
memfd_create
是一个 Linux 系统调用,用于创建一个匿名的内存文件描述符。这个文件描述符指向一个基于内存的文件,可以在进程间共享,支持文件操作(如 read
、write
、mmap
等),但不会在文件系统中创建实际的文件。
2. 函数原型 見出しへのリンク
#define _GNU_SOURCE
#include <sys/mman.h>
int memfd_create(const char *name, unsigned int flags);
3. 功能 見出しへのリンク
创建一个基于内存的匿名文件描述符,该文件存在于内存中,具有普通文件的语义,但不会出现在文件系统中。创建的文件描述符可以用于进程间通信、共享内存、临时文件存储等场景。
4. 参数 見出しへのリンク
const char *name
: 文件描述符的名称(主要用于调试和标识)- 仅用于调试目的,不会创建实际的文件系统条目
- 可以在
/proc/self/fd/
中看到这个名称
unsigned int flags
: 控制标志MFD_CLOEXEC
: 设置 close-on-exec 标志MFD_ALLOW_SEALING
: 允许对文件应用密封(sealing)MFD_HUGETLB
: 使用大页内存(可选,需要特定内核支持)MFD_HUGE_*
: 指定大页大小(如MFD_HUGE_2MB
)
5. 返回值 見出しへのリンク
- 成功时返回新的文件描述符(非负整数)
- 失败时返回 -1,并设置
errno
6. 常见 errno 错误码 見出しへのリンク
EINVAL
:flags
参数无效EMFILE
: 进程已打开的文件描述符达到上限ENFILE
: 系统已打开的文件描述符达到上限ENOMEM
: 内存不足EPERM
: 权限不足(某些标志需要特殊权限)ENOSYS
: 系统不支持此系统调用
7. 相似函数,或关联函数 見出しへのリンク
shm_open()
: POSIX 共享内存对象mkstemp()
: 创建临时文件tmpfile()
: 创建临时文件流mmap()
: 内存映射文件ftruncate()
: 设置文件大小fcntl()
: 文件描述符控制/proc/[pid]/fd/
: 查看进程打开的文件描述符
8. 示例代码 見出しへのリンク
示例1:基本使用 - 创建和使用内存文件 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main() {
const char *memfd_name = "example_memfd";
int memfd;
printf("=== memfd_create 基本使用演示 ===\n");
// 创建内存文件描述符
memfd = memfd_create(memfd_name, MFD_CLOEXEC);
if (memfd == -1) {
if (errno == ENOSYS) {
printf("错误: 系统不支持 memfd_create (需要 Linux 3.17+)\n");
exit(EXIT_FAILURE);
} else {
perror("memfd_create 失败");
exit(EXIT_FAILURE);
}
}
printf("✓ 成功创建内存文件描述符: %d\n", memfd);
printf(" 名称: %s\n", memfd_name);
// 设置文件大小
off_t file_size = 4096; // 4KB
if (ftruncate(memfd, file_size) == -1) {
perror("ftruncate 失败");
close(memfd);
exit(EXIT_FAILURE);
}
printf("✓ 设置文件大小为 %ld 字节\n", (long)file_size);
// 内存映射文件
char *mapped_memory = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
MAP_SHARED, memfd, 0);
if (mapped_memory == MAP_FAILED) {
perror("mmap 失败");
close(memfd);
exit(EXIT_FAILURE);
}
printf("✓ 成功映射内存区域\n");
// 写入数据到内存文件
const char *data = "Hello, memfd! This is data stored in memory file.";
strcpy(mapped_memory, data);
printf("✓ 写入数据: %s\n", data);
// 通过文件描述符写入更多数据
const char *more_data = "\nAdditional data written via write().";
if (write(memfd, more_data, strlen(more_data)) == -1) {
perror("write 失败");
} else {
printf("✓ 通过 write() 写入额外数据\n");
}
// 读取数据验证
char read_buffer[512];
if (pread(memfd, read_buffer, sizeof(read_buffer) - 1, 0) != -1) {
read_buffer[sizeof(read_buffer) - 1] = '\0';
printf("✓ 读取数据: %s\n", read_buffer);
}
// 显示文件状态信息
struct stat st;
if (fstat(memfd, &st) == 0) {
printf("\n文件状态信息:\n");
printf(" 文件大小: %ld 字节\n", (long)st.st_size);
printf(" inode: %ld\n", (long)st.st_ino);
printf(" 链接数: %ld\n", (long)st.st_nlink);
printf(" 权限: %o\n", st.st_mode & 0777);
}
// 查看 /proc 中的文件描述符信息
printf("\n/proc/self/fd/ 中的信息:\n");
char proc_path[64];
snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", memfd);
char link_target[256];
ssize_t len = readlink(proc_path, link_target, sizeof(link_target) - 1);
if (len != -1) {
link_target[len] = '\0';
printf(" %s -> %s\n", proc_path, link_target);
}
// 清理资源
munmap(mapped_memory, file_size);
close(memfd);
printf("\n✓ 成功完成 memfd 演示\n");
return 0;
}
示例2:密封功能演示 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <linux/fcntl.h>
#ifndef F_ADD_SEALS
# define F_ADD_SEALS 1024
# define F_GET_SEALS 1025
# define F_SEAL_SEAL 0x0001
# define F_SEAL_SHRINK 0x0002
# define F_SEAL_GROW 0x0004
# define F_SEAL_WRITE 0x0008
#endif
void demonstrate_sealing() {
printf("=== memfd 密封功能演示 ===\n");
// 创建允许密封的内存文件
int memfd = memfd_create("sealed_memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (memfd == -1) {
perror("memfd_create 失败");
return;
}
printf("✓ 创建允许密封的内存文件: %d\n", memfd);
// 写入初始数据
const char *initial_data = "Initial data before sealing";
if (write(memfd, initial_data, strlen(initial_data)) == -1) {
perror("初始写入失败");
close(memfd);
return;
}
printf("✓ 写入初始数据: %s\n", initial_data);
// 获取当前密封状态
int seals = fcntl(memfd, F_GET_SEALS);
if (seals == -1) {
perror("获取密封状态失败");
} else {
printf("初始密封状态: 0x%x\n", seals);
}
// 应用密封
int new_seals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE;
if (fcntl(memfd, F_ADD_SEALS, new_seals) == -1) {
perror("应用密封失败");
} else {
printf("✓ 成功应用密封: SHRINK | GROW | WRITE\n");
}
// 验证密封效果
seals = fcntl(memfd, F_GET_SEALS);
if (seals != -1) {
printf("当前密封状态: 0x%x\n", seals);
if (seals & F_SEAL_WRITE) printf(" - 禁止写入\n");
if (seals & F_SEAL_SHRINK) printf(" - 禁止缩小\n");
if (seals & F_SEAL_GROW) printf(" - 禁止增长\n");
if (seals & F_SEAL_SEAL) printf(" - 禁止进一步密封\n");
}
// 尝试违反密封的操作
printf("\n测试密封效果:\n");
// 尝试写入(应该失败)
const char *new_data = "New data";
if (write(memfd, new_data, strlen(new_data)) == -1) {
printf("✓ 写入操作被阻止: %s\n", strerror(errno));
}
// 尝试缩小文件(应该失败)
if (ftruncate(memfd, 10) == -1) {
printf("✓ 缩小文件被阻止: %s\n", strerror(errno));
}
// 尝试增长文件(应该失败)
if (ftruncate(memfd, 1000) == -1) {
printf("✓ 增长文件被阻止: %s\n", strerror(errno));
}
// 读取数据应该仍然可以工作
char buffer[256];
if (pread(memfd, buffer, sizeof(buffer) - 1, 0) != -1) {
buffer[sizeof(buffer) - 1] = '\0';
printf("✓ 读取操作仍然可用: %s\n", buffer);
}
close(memfd);
}
int main() {
printf("=== memfd_create 密封功能演示 ===\n");
// 检查系统支持
int test_fd = memfd_create("test", MFD_ALLOW_SEALING);
if (test_fd == -1) {
if (errno == ENOSYS) {
printf("错误: 系统不支持 memfd_create\n");
return 1;
}
} else {
close(test_fd);
demonstrate_sealing();
}
return 0;
}
示例3:进程间共享演示 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
void parent_process(int memfd) {
printf("父进程 (PID: %d):\n", getpid());
// 设置文件大小
if (ftruncate(memfd, 4096) == -1) {
perror("ftruncate 失败");
return;
}
// 内存映射
char *shared_memory = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_SHARED, memfd, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap 失败");
return;
}
// 写入数据
const char *message = "Hello from parent process!";
strcpy(shared_memory, message);
printf(" 写入共享内存: %s\n", message);
// 等待子进程
sleep(2);
// 读取子进程写入的数据
printf(" 从共享内存读取: %s\n", shared_memory);
munmap(shared_memory, 4096);
}
void child_process(int memfd) {
printf("子进程 (PID: %d):\n", getpid());
// 等待父进程写入数据
sleep(1);
// 内存映射
char *shared_memory = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_SHARED, memfd, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap 失败");
return;
}
// 读取父进程的数据
printf(" 从共享内存读取: %s\n", shared_memory);
// 写入回复数据
const char *reply = "Hello from child process!";
strcpy(shared_memory, reply);
printf(" 写入共享内存: %s\n", reply);
munmap(shared_memory, 4096);
}
int main() {
printf("=== memfd 进程间共享演示 ===\n");
// 创建内存文件描述符
int memfd = memfd_create("shared_memfd", MFD_CLOEXEC);
if (memfd == -1) {
if (errno == ENOSYS) {
printf("错误: 系统不支持 memfd_create\n");
return 1;
} else {
perror("memfd_create 失败");
return 1;
}
}
printf("✓ 创建共享内存文件描述符: %d\n", memfd);
// 创建子进程
pid_t pid = fork();
if (pid == -1) {
perror("fork 失败");
close(memfd);
return 1;
}
if (pid == 0) {
// 子进程
child_process(memfd);
close(memfd);
exit(0);
} else {
// 父进程
parent_process(memfd);
// 等待子进程结束
int status;
waitpid(pid, &status, 0);
close(memfd);
printf("✓ 进程间共享演示完成\n");
}
return 0;
}
示例4:高级功能和错误处理 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
void test_memfd_limits() {
printf("=== memfd 限制测试 ===\n");
// 测试大量 memfd 创建
printf("创建多个 memfd 实例:\n");
int memfds[10];
int created_count = 0;
for (int i = 0; i < 10; i++) {
char name[32];
snprintf(name, sizeof(name), "test_memfd_%d", i);
memfds[i] = memfd_create(name, MFD_CLOEXEC);
if (memfds[i] != -1) {
printf(" ✓ 创建 %s: fd=%d\n", name, memfds[i]);
created_count++;
} else {
printf(" ✗ 创建 %s 失败: %s\n", name, strerror(errno));
break;
}
}
printf("成功创建 %d 个 memfd 实例\n", created_count);
// 清理
for (int i = 0; i < created_count; i++) {
close(memfds[i]);
}
}
void test_error_conditions() {
printf("\n=== 错误条件测试 ===\n");
// 测试无效标志
printf("测试无效标志:\n");
int fd = memfd_create("test", 0xFFFFFFFF);
if (fd == -1) {
printf(" ✓ 无效标志被拒绝: %s\n", strerror(errno));
}
// 测试 NULL 名称
printf("测试 NULL 名称:\n");
fd = memfd_create(NULL, 0);
if (fd == -1) {
printf(" 结果: %s\n", strerror(errno));
} else {
printf(" ✓ NULL 名称被接受\n");
close(fd);
}
// 测试空名称
printf("测试空名称:\n");
fd = memfd_create("", 0);
if (fd == -1) {
printf(" 结果: %s\n", strerror(errno));
} else {
printf(" ✓ 空名称被接受\n");
close(fd);
}
}
void demonstrate_features() {
printf("\n=== memfd 特性演示 ===\n");
// 创建基本 memfd
int memfd = memfd_create("feature_test", MFD_CLOEXEC);
if (memfd == -1) {
perror("memfd_create 失败");
return;
}
printf("✓ 创建 memfd: %d\n", memfd);
// 测试文件操作
printf("文件操作测试:\n");
// 写入数据
const char *data = "Test data for various operations";
ssize_t written = write(memfd, data, strlen(data));
if (written != -1) {
printf(" ✓ 写入 %zd 字节\n", written);
}
// 读取数据
char buffer[256];
lseek(memfd, 0, SEEK_SET); // 重置文件位置
ssize_t read_bytes = read(memfd, buffer, sizeof(buffer) - 1);
if (read_bytes != -1) {
buffer[read_bytes] = '\0';
printf(" ✓ 读取 %zd 字节: %s\n", read_bytes, buffer);
}
// 设置文件大小
if (ftruncate(memfd, 8192) == 0) {
printf(" ✓ 设置文件大小为 8192 字节\n");
}
// 获取文件状态
struct stat st;
if (fstat(memfd, &st) == 0) {
printf(" ✓ 文件状态 - 大小: %ld, inode: %ld\n",
(long)st.st_size, (long)st.st_ino);
}
// 内存映射
char *mapped = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
if (mapped != MAP_FAILED) {
printf(" ✓ 内存映射成功\n");
strcpy(mapped, "Mapped memory data");
printf(" ✓ 写入映射内存: %s\n", mapped);
munmap(mapped, 8192);
}
// 复制文件描述符
int dup_fd = dup(memfd);
if (dup_fd != -1) {
printf(" ✓ 复制文件描述符: %d -> %d\n", memfd, dup_fd);
close(dup_fd);
}
close(memfd);
}
void interactive_memfd_tool() {
char name[256];
int choice;
int current_memfd = -1;
while (1) {
printf("\n=== memfd 交互式工具 ===\n");
printf("1. 创建 memfd\n");
printf("2. 设置文件大小\n");
printf("3. 写入数据\n");
printf("4. 读取数据\n");
printf("5. 显示文件信息\n");
printf("6. 内存映射\n");
printf("7. 关闭 memfd\n");
printf("0. 退出\n");
printf("请选择操作: ");
if (scanf("%d", &choice) != 1) {
printf("输入无效\n");
while (getchar() != '\n'); // 清空输入缓冲区
continue;
}
switch (choice) {
case 1:
printf("输入 memfd 名称: ");
scanf("%255s", name);
if (current_memfd != -1) {
close(current_memfd);
}
current_memfd = memfd_create(name, MFD_CLOEXEC);
if (current_memfd != -1) {
printf("✓ 成功创建 memfd: %d\n", current_memfd);
} else {
printf("✗ 创建失败: %s\n", strerror(errno));
}
break;
case 2: {
if (current_memfd == -1) {
printf("请先创建 memfd\n");
break;
}
off_t size;
printf("输入文件大小 (字节): ");
if (scanf("%ld", &size) == 1) {
if (ftruncate(current_memfd, size) == 0) {
printf("✓ 设置文件大小为 %ld 字节\n", (long)size);
} else {
printf("✗ 设置失败: %s\n", strerror(errno));
}
}
break;
}
case 3: {
if (current_memfd == -1) {
printf("请先创建 memfd\n");
break;
}
char data[256];
printf("输入要写入的数据: ");
scanf("%255s", data);
ssize_t written = write(current_memfd, data, strlen(data));
if (written != -1) {
printf("✓ 写入 %zd 字节\n", written);
} else {
printf("✗ 写入失败: %s\n", strerror(errno));
}
break;
}
case 4: {
if (current_memfd == -1) {
printf("请先创建 memfd\n");
break;
}
char buffer[256];
lseek(current_memfd, 0, SEEK_SET);
ssize_t read_bytes = read(current_memfd, buffer, sizeof(buffer) - 1);
if (read_bytes != -1) {
buffer[read_bytes] = '\0';
printf("✓ 读取数据: %s\n", buffer);
} else {
printf("✗ 读取失败: %s\n", strerror(errno));
}
break;
}
case 5: {
if (current_memfd == -1) {
printf("请先创建 memfd\n");
break;
}
struct stat st;
if (fstat(current_memfd, &st) == 0) {
printf("文件信息:\n");
printf(" 文件描述符: %d\n", current_memfd);
printf(" 文件大小: %ld 字节\n", (long)st.st_size);
printf(" inode: %ld\n", (long)st.st_ino);
printf(" 权限: %o\n", st.st_mode & 0777);
} else {
printf("✗ 获取文件信息失败: %s\n", strerror(errno));
}
break;
}
case 6: {
if (current_memfd == -1) {
printf("请先创建 memfd\n");
break;
}
struct stat st;
if (fstat(current_memfd, &st) == 0 && st.st_size > 0) {
char *mapped = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, current_memfd, 0);
if (mapped != MAP_FAILED) {
printf("✓ 内存映射成功,大小: %ld 字节\n", (long)st.st_size);
printf(" 前64字节内容: %.64s\n", mapped);
munmap(mapped, st.st_size);
} else {
printf("✗ 内存映射失败: %s\n", strerror(errno));
}
} else {
printf("文件大小为0或无法获取文件信息\n");
}
break;
}
case 7:
if (current_memfd != -1) {
close(current_memfd);
current_memfd = -1;
printf("✓ 关闭 memfd\n");
} else {
printf("没有打开的 memfd\n");
}
break;
case 0:
if (current_memfd != -1) {
close(current_memfd);
}
printf("退出 memfd 工具\n");
return;
default:
printf("无效选择\n");
break;
}
}
}
int main() {
printf("=== memfd_create 高级功能演示 ===\n");
// 检查系统支持
int test_fd = memfd_create("test", 0);
if (test_fd == -1) {
if (errno == ENOSYS) {
printf("错误: 系统不支持 memfd_create (需要 Linux 3.17+)\n");
return 1;
}
} else {
close(test_fd);
printf("✓ 系统支持 memfd_create\n");
}
// 演示各种功能
demonstrate_features();
test_memfd_limits();
test_error_conditions();
// 启动交互式工具
char choice;
printf("\n是否启动交互式 memfd 工具? (y/N): ");
if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
interactive_memfd_tool();
}
return 0;
}
9. memfd 标志说明 見出しへのリンク
// memfd_create 支持的标志:
// MFD_CLOEXEC
// • 设置 close-on-exec 标志
// • 子进程 exec 时自动关闭文件描述符
// • 推荐总是设置此标志
// MFD_ALLOW_SEALING
// • 允许对文件应用密封
// • 后续可以使用 fcntl() 添加密封
// • 提供额外的安全控制
// MFD_HUGETLB (可选)
// • 使用大页内存
// • 需要内核支持和适当权限
// • 可以指定具体的大页大小
// 大页大小标志:
// MFD_HUGE_64KB, MFD_HUGE_512KB, MFD_HUGE_1MB,
// MFD_HUGE_2MB, MFD_HUGE_8MB, MFD_HUGE_16MB,
// MFD_HUGE_32MB, MFD_HUGE_256MB, MFD_HUGE_512MB,
// MFD_HUGE_1GB, MFD_HUGE_2GB, MFD_HUGE_16GB
10. 实际应用场景 見出しへのリンク
场景1:安全的临时文件存储 見出しへのリンク
int create_secure_temp_storage() {
// 创建不显示在文件系统中的临时存储
int memfd = memfd_create("secure_temp", MFD_CLOEXEC);
if (memfd != -1) {
// 设置适当大小
ftruncate(memfd, 1024 * 1024); // 1MB
return memfd;
}
return -1;
}
场景2:进程间通信 見出しへのリンク
int setup_shared_memory_communication() {
// 创建用于进程间通信的共享内存
int memfd = memfd_create("ipc_shared", MFD_CLOEXEC);
if (memfd != -1) {
// 设置大小并映射
ftruncate(memfd, 4096);
// 可以通过 Unix 域套接字传递文件描述符给其他进程
}
return memfd;
}
场景3:动态库加载 見出しへのリンク
int load_dynamic_library_from_memory() {
// 创建内存文件用于动态库
int memfd = memfd_create("dynamic_lib", MFD_CLOEXEC);
if (memfd != -1) {
// 将库代码写入内存文件
// 可以使用 dlopen() 加载
}
return memfd;
}
11. 注意事项 見出しへのリンク
使用 memfd_create
时需要注意:
- 内核版本: 需要 Linux 3.17 或更高版本
- 权限要求: 通常不需要特殊权限
- 资源管理: 及时关闭文件描述符避免资源泄漏
- 内存使用: 内存文件占用物理内存
- 安全性: 不会出现在文件系统中,相对安全
- 可移植性: Linux 特有的功能
12. 与相关技术的比较 見出しへのリンク
// memfd vs tmpfile():
// • memfd 不创建文件系统条目
// • tmpfile() 创建临时文件(通常在 /tmp)
// • memfd 更安全,tmpfile() 可能被访问
// memfd vs shm_open():
// • memfd 是 Linux 特有的
// • shm_open() 是 POSIX 标准
// • memfd 更简单,不需要命名
// memfd vs mmap(MAP_ANONYMOUS):
// • memfd 提供文件描述符
// • MAP_ANONYMOUS 直接映射匿名内存
// • memfd 可以传递给其他进程
13. 系统配置检查 見出しへのリンク
# 检查内核版本
uname -r
# 检查 memfd 支持
grep -i memfd /boot/config-$(uname -r)
# 查看进程的 memfd 文件描述符
ls -la /proc/self/fd/ | grep memfd
# 检查系统调用表
ausyscall memfd_create
# 查看内存使用情况
cat /proc/meminfo
总结 見出しへのリンク
memfd_create
是 Linux 系统中创建匿名内存文件的强大工具:
关键特性:
- 内存存储: 文件完全存储在内存中
- 文件语义: 支持完整的文件操作接口
- 安全特性: 不出现在文件系统中
- 进程共享: 可以在进程间传递和共享
- 密封支持: 可以应用安全密封
主要应用:
- 安全的临时文件存储
- 进程间通信和共享内存
- 动态库和代码加载
- 高性能缓存和缓冲区
- 容器和沙箱环境
使用要点:
- 检查系统支持(Linux 3.17+)
- 合理设置标志(推荐 MFD_CLOEXEC)
- 正确管理文件描述符生命周期
- 考虑内存使用量
- 利用密封功能增强安全性
memfd_create
为现代 Linux 应用程序提供了灵活、安全的内存文件解决方案,是系统编程中的重要工具。