Linux splice () возвращает EINVAL («Неверный аргумент») - PullRequest
4 голосов
/ 17 августа 2011

Я пытаюсь поэкспериментировать с использованием соединения ( man 2 splice ) для копирования данных из UDP-сокета прямо в файл.К сожалению, при первом вызове splice () возвращается EINVAL.

Страница man заявляет:

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.

Ответы [ 2 ]

5 голосов
/ 17 августа 2011

Да, он определенно не поддерживается для чтения из сокета UDP, даже в последних версиях ядра.Ниже приведены ссылки на исходные тексты ядра.

splice вызывает do_splice в ядре, которое вызывает do_splice_to, что вызывает член splice_read в структуре file_operations дляфайл.

Для сокетов эта структура определяется как socket_file_ops в 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 в net / ipv4 / af_inet.c .Наконец, эта структура явно не инициализирует поле splice_read поля struct proto_ops;то есть он инициализирует его нулем.

Так что sock_splice_read возвращает -EINVAL, и это распространяется вверх.

5 голосов
/ 17 августа 2011

нашел мой ответ. tl; dr - UDP не поддерживается на входящей стороне.

После достаточного поиска в Google, я наткнулся на обсуждение на форуме и некоторый тестовый код , который выводит таблицу типов fd in / out и их поддержку:

$ ./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
...