使用管道将/proc/self/smaps复制到memfd文件时splice报EINVAL错误
问题分析:splice调用返回EINVAL的原因及解决方法
核心问题
第一个splice调用返回EINVAL,根源在于proc文件系统的虚拟文件不支持作为splice的源端:
splice要求参与操作的文件系统实现对应的splice_read/splice_write接口,而/proc/self/smaps是内核动态生成的虚拟文件,proc文件系统并未实现splice_read方法。- 当尝试从proc文件描述符向管道
splice数据时,内核因无法处理该操作,直接返回EINVAL错误。
修正后的代码
将第一个proc→管道的splice替换为传统的read+write操作,绕开proc文件的限制,同时保留后续管道→memfd的splice逻辑:
#include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> void err(int status, char const* ctx) { if (status == -1) { fprintf(stderr, "Error %s: %s\n", ctx, strerrorname_np(errno)); exit(1); } } #define MB 1024 * 1024 unsigned copy_fds(int fd_target, int fd_source) { int pipefd[2]; int status; status = pipe2(pipefd, 0); err(status, "creating pipe"); status = fcntl(pipefd[1], F_SETPIPE_SZ, 1 * MB); err(status, "allocating buf for the pipe"); status = ftruncate(fd_target, 1 * MB); err(status, "allocating space for the memfd-file"); // 改用read+write读取proc文件并写入管道,规避proc不支持splice的限制 char buf[4096]; ssize_t total_transferred = 0; ssize_t read_len; while ((read_len = read(fd_source, buf, sizeof(buf))) > 0) { ssize_t write_len = write(pipefd[1], buf, read_len); err(write_len, "write to pipe"); total_transferred += write_len; } err(read_len, "read from smaps"); // 关闭管道写端,通知splice数据已写完 close(pipefd[1]); ssize_t written = splice(pipefd[0], NULL, fd_target, NULL, total_transferred, 0); err(written, "splice to memfd"); close(pipefd[0]); return written; } int main() { int fd_smaps = open("/proc/self/smaps", O_RDONLY); err(fd_smaps, "opening smaps"); int fd_tmp = memfd_create("smaps", 0); err(fd_tmp, "creating memfd"); printf("Copied: %u\n", copy_fds(fd_tmp, fd_smaps)); close(fd_smaps); close(fd_tmp); return 0; }
关键说明
read+write是proc文件的通用读取方式,不受文件系统接口限制。- 关闭管道写端后,
splice会在读完管道内所有数据后终止,避免无限等待。 - memfd属于tmpfs文件系统,完全支持
splice写入操作,因此第二个splice调用可以正常执行。
内容的提问来源于stack exchange,提问作者ABu
相关产品推荐
相关产品推荐

