4

我正在尝试使用 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 由以下伪代码初始化:

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 套接字是否热。

4

2 回答 2

6

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

经过足够的谷歌搜索后,我偶然发现了一个论坛讨论和一些测试代码,这些代码打印出输入/输出 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
于 2011-08-17T04:44:29.227 回答
6

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

splicedo_splice在内核中调用,它调用do_splice_to,它调用文件结构中的splice_read成员。file_operations

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

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

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

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

所以sock_splice_read返回-EINVAL,并且向上传播。

于 2011-08-17T04:59:09.027 回答