You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

调用splice(2)读取ftrace原始文件返回"Invalid argument"问题求助

问题分析与解决方案

我来帮你拆解这个问题:你尝试用splice(2)零拷贝读取ftrace的_raw跟踪文件时遇到Invalid argument错误,但不带_raw后缀的对应文件可以正常splice,同时read()cattrace-cmd都能正常读取这些_raw文件。

核心原因

ftrace的_raw文件(比如trace_pipe_rawsnapshot_raw)是debugfs下的特殊节点,它们的内核实现没有提供splice所需的iter_file_splice_read接口splice要求源文件描述符对应的文件系统支持直接的拼接操作,而这些_raw节点只实现了常规的read接口。当你调用splice时,内核找不到对应的splice处理逻辑,就会返回EINVAL错误。

而不带_raw后缀的文件是经过格式化的文本流,对应的内核实现完整支持splice操作,所以可以正常使用。

为什么read()cattrace-cmd能正常工作?

这些工具/系统调用都是用常规的read()接口来读取文件内容,不需要依赖splice的特殊支持,所以不受_raw文件的限制。trace-cmd本身就是为解析ftrace原始二进制数据设计的,内部也是通过read()来读取_raw文件的。

解决方案

如果你的需求是读取_raw文件内容,同时尽量保留高效的操作,可以这样调整:

  1. 放弃对_raw文件直接使用splice,改用read()读取数据到临时缓冲区,再写入管道,最后用splice将管道数据写入目标文件(虽然这一步失去了完全零拷贝的优势,但能解决兼容性问题)。
  2. 如果不需要零拷贝,直接用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

火山引擎 最新活动