Linux splice() return EINVAL ( "Invalid argument" )



我正在尝试使用拼接(人 2 拼接(将数据从 UDP 套接字直接复制到文件。 不幸的是,对splice((的第一次调用返回了EINVAL。

手册页指出:

EINVAL Target file system doesn't support splicing; target file is opened in
       append mode; neither of the descriptors refers to a pipe; or offset
       given for nonseekable device.

但是,我认为这些条件都不适用。 我使用的是Fedora 15(内核2.6.40-4(,所以我相信所有文件系统都支持splice((。 目标文件在第一次调用拼接时应该无关紧要,但为了完整起见,我通过 open(path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) 打开它。 两个调用都使用管道,并且除了 NULL 之外,两个调用都不使用偏移量。

这是我的示例代码:

int sz = splice(sock_fd, 0, mPipeFds[1], 0, 8192, SPLICE_F_MORE);
if (-1 == sz)
{
int err = errno;
LOG4CXX_ERROR(spLogger, "splice from: " << strerror(err));
return 0;
}
sz = splice(mPipeFds[0], 0, file_fd, 0, sz, SPLICE_F_MORE);
if (-1 == sz)
{
int err = errno;
LOG4CXX_ERROR(spLogger, "splice to: " << strerror(err));
}
return 0;

sock_fd由以下伪代码初始化:

int sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK);
bind(sock_fd, ...);

可能相关的是,此代码片段在 libevent 循环中运行。 libevent正在使用epoll((来确定UDP套接字是否过热。

找到了我的答案。 tl;dr - 入站端不支持 UDP。

经过足够的谷歌搜索,我偶然发现了一个论坛讨论和一些测试代码,其中打印出一个输入/输出 fd 类型及其支持的表格:

$ ./a.out 
inout     pipe    reg     chr     unix    tcp    udp
pipe       yes     yes     yes     yes     yes    yes
reg        yes     no      no      no      no     no
chr        yes     no      no      no      no     no
unix       no      no      no      no      no     no
tcp        yes     no      no      no      no     no
udp        no      no      no      no      no     no

是的,即使在最新的内核中,也绝对不支持从 UDP 套接字读取。 对内核源代码的引用如下。

splice调用内核中的do_splice,内核调用 do_splice_to ,后者调用文件的file_operations结构中的splice_read成员。

对于套接字,该结构在 net/socket.c 中定义为socket_file_ops,这会将splice_read字段初始化为 sock_splice_read

反过来,该函数包含以下代码行:

if (unlikely(!sock->ops->splice_read))
    return -EINVAL;

套接字的ops字段是一个struct proto_ops。 对于 IPv4 UDP 套接字,它初始化为 net/ipv4/af_inet.c 中的inet_dgram_ops。 最后,该结构没有显式初始化struct proto_opssplice_read字段;即,它将其初始化为零。

所以sock_splice_read返回 -EINVAL,然后向上传播。

相关内容

  • 没有找到相关文章

最新更新