我正在尝试使用splice(man 2 splice)来将数据从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()。目标文件在第一次调用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由以下psuedocode初始化:
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套接字是否很热。
答案 0 :(得分:5)
找到我的答案。 tl; dr - 入站端不支持UDP。
经过足够的谷歌搜索后,我偶然发现了一个forum discussion和一些test code,它打印出一个输入/输出fd类型的表格及其支持:
$ ./a.out
in\out 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
答案 1 :(得分:5)
是的,即使在最新的内核中,也绝不支持从UDP套接字读取。引用内核源代码如下。
splice
调用do_splice
in the kernel, which calls do_splice_to
,调用文件splice_read
结构中的file_operations
成员。
对于套接字,该结构定义为socket_file_ops in net/socket.c,将splice_read
字段初始化为sock_splice_read
。
该函数反过来包含以下代码行:
if (unlikely(!sock->ops->splice_read))
return -EINVAL;
套接字的ops
字段是struct proto_ops
。对于IPv4 UDP套接字,它初始化为inet_dgram_ops
in net/ipv4/af_inet.c。最后,该结构未明确初始化splice_read
的{{1}}字段;即,它将其初始化为零。
所以struct proto_ops
返回-EINVAL,然后传播。