2011-08-16 4 views
4

splice (man 2 splice)를 사용하여 데이터를 UDP 소켓에서 직접 파일로 복사하려고합니다. 아쉽게도 splice()에 대한 첫 번째 호출은 EINVAL을 반환합니다.EINVAL ("Invalid argument")를 반환하는 리눅스 splice()

남자 페이지 상태 :

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 (kernel 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

'sock_fd' 초기화가 끔찍한 것 같습니다. 반환 값을 확인하십시오! –

+0

그건 그냥 psuedocode입니다. 실제 코드는 않습니다. 나는 약간의 텍스트를 명확히했다. –

답변

5

내 대답을 찾았습니다. tl; dr - 인바운드 측에서 UDP가 지원되지 않습니다.

충분히 인터넷 검색 후 나는 forum discussion 일부 인/아웃 FD 종류의 테이블을 출력 test code과 지원을 우연히 :

$ ./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은 파일의 file_operations 구조의 splice_read 멤버를 호출하는 do_splice in the kernel, which calls do_splice_to을 호출합니다.

소켓의 경우 해당 구조는 으로 정의되어 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 필드를 struct proto_ops으로 초기화하지 않습니다. 즉, 그것을 0으로 초기화한다.

그래서 sock_splice_read은 -EINVAL을 반환하며이를 전파합니다.