2011-08-16 126 views
4

我試圖使用拼接(man 2 splice)將數據從UDP套接字直接複製到文件。不幸的是,第一次調用splice()會返回EINVAL。Linux 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套接字是否很熱。

+0

'sock_fd'初始化看起來很可怕。檢查返回值! –

+0

這只是psuedocode。實際的代碼確實如此。我已經澄清了一點。 –

回答

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

如果這段代碼可以在這裏複製,這將是非常好的,以防止它永遠丟失。 – Aktau

4

是的,這肯定是不支持從UDP套接字讀取,即使在最新的內核中。對內核源碼的引用如下。

splice調用do_splice in the kernel, which calls do_splice_to,該文件調用file_operations結構中的splice_read成員。

對於套接字,該結構定義爲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。最後,該結構沒有明確地初始化struct proto_opssplice_read字段;即它將它初始化爲零。因此sock_splice_read返回-EINVAL,並傳播。