调用splice(2)读取ftrace原始文件返回"Invalid argument"问题求助
问题分析与解决方案
我来帮你拆解这个问题:你尝试用splice(2)零拷贝读取ftrace的_raw跟踪文件时遇到Invalid argument错误,但不带_raw后缀的对应文件可以正常splice,同时read()、cat和trace-cmd都能正常读取这些_raw文件。
核心原因
ftrace的_raw文件(比如trace_pipe_raw、snapshot_raw)是debugfs下的特殊节点,它们的内核实现没有提供splice所需的iter_file_splice_read接口。splice要求源文件描述符对应的文件系统支持直接的拼接操作,而这些_raw节点只实现了常规的read接口。当你调用splice时,内核找不到对应的splice处理逻辑,就会返回EINVAL错误。
而不带_raw后缀的文件是经过格式化的文本流,对应的内核实现完整支持splice操作,所以可以正常使用。
为什么read()、cat和trace-cmd能正常工作?
这些工具/系统调用都是用常规的read()接口来读取文件内容,不需要依赖splice的特殊支持,所以不受_raw文件的限制。trace-cmd本身就是为解析ftrace原始二进制数据设计的,内部也是通过read()来读取_raw文件的。
解决方案
如果你的需求是读取_raw文件内容,同时尽量保留高效的操作,可以这样调整:
- 放弃对
_raw文件直接使用splice,改用read()读取数据到临时缓冲区,再写入管道,最后用splice将管道数据写入目标文件(虽然这一步失去了完全零拷贝的优势,但能解决兼容性问题)。 - 如果不需要零拷贝,直接用
read()+write()完成文件复制即可。
以下是修改后的测试代码示例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <stdexcept> #include <string> static void unit_test_x(void) { int buffer_pipe[2]; if (pipe(buffer_pipe) == -1) { perror("pipe failed"); throw std::runtime_error("pipe creation failed"); } std::string source_path = "/sys/kernel/debug/tracing/per_cpu/cpu1/trace_pipe_raw"; int trace_fd = open(source_path.c_str(), O_RDONLY); if (trace_fd == -1) { perror("open trace_pipe_raw failed"); throw std::runtime_error("opening source file failed"); } std::string destination_path = "foo"; int dest_fd = open(destination_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); if (dest_fd == -1) { perror("open destination file failed"); close(trace_fd); throw std::runtime_error("opening destination file failed"); } // 替换splice为read+write到管道,适配_raw文件的接口限制 char buf[1000]; int actually_read = read(trace_fd, buf, sizeof(buf)); if (actually_read < 0) { printf("Oh dear, something went wrong %s\n", strerror(errno)); close(trace_fd); close(dest_fd); close(buffer_pipe[0]); close(buffer_pipe[1]); throw std::runtime_error("reading from source failed"); } else if (actually_read == 0) { // 源文件已到EOF,直接清理退出 close(trace_fd); close(dest_fd); close(buffer_pipe[0]); close(buffer_pipe[1]); return; } int written = write(buffer_pipe[1], buf, actually_read); if (written != actually_read) { printf("Failed to write all data to pipe: wrote %d instead of %d\n", written, actually_read); close(trace_fd); close(dest_fd); close(buffer_pipe[0]); close(buffer_pipe[1]); throw std::runtime_error("writing to pipe failed"); } // 从管道splice到目标文件 int spliced = splice(buffer_pipe[0], NULL, dest_fd, NULL, actually_read, SPLICE_F_MORE | SPLICE_F_MOVE); if (spliced < 0) { printf("Oh dear, something went wrong %s\n", strerror(errno)); close(trace_fd); close(dest_fd); close(buffer_pipe[0]); close(buffer_pipe[1]); throw std::runtime_error("splicing from pipe to destination failed"); } // 关闭所有文件描述符 close(trace_fd); close(dest_fd); close(buffer_pipe[0]); close(buffer_pipe[1]); }
内容的提问来源于stack exchange,提问作者p3t3




