Подтвердить что ты не робот

Ли Linux имеет нулевую копию? splice или sendfile?

При вставке сплайсинга в списке ядра обсуждался, что sendfile был повторно реализован на основе сплайсинга. В документации для сращивания SLICE_F_MOVE указано:

Попытка перемещения страниц вместо копирования. Это     только подсказка для ядра: страницы все равно могут быть     копируется, если ядро ​​не может перемещать страницы из     трубы, или если буферы труб не относятся к     полные страницы. Первоначальная реализация этого     флаг был глючит: поэтому, начиная с Linux 2.6.21     это нет-op (но все еще разрешено в     вызов splice()); в будущем, правильный     реализация может быть восстановлена.

Значит ли это, что Linux не имеет метода нулевой копии для записи в сокеты? Или это было зафиксировано в какой-то момент, и никто не обновлял документацию в течение многих лет? Имеет ли какой-либо из sendfile или splice нулевую реализацию копии в любой из последних версий ядра 3.x?

Поскольку у Google нет ответа на этот запрос, я создаю вопрос о stackoverflow для следующего бедного schmuck, который хочет знать, есть ли какая-либо польза для использования vmsplice и splice или sendfile поверх простой старой записи.

4b9b3361

Ответ 1

sendfile был с тех пор и по-прежнему является нулевым (предполагается, что аппаратное обеспечение позволяет это, но это обычно имеет место). В качестве нулевой копии была вся суть этого сценария в первую очередь. sendfile теперь реализуется как обертка вокруг splice.

Это говорит о том, что splice тоже имеет нулевую копию, и это действительно так. По крайней мере, теоретически и, по крайней мере, в некоторых случаях. Проблема заключается в том, как правильно использовать его, чтобы он работал надежно, и поэтому он имеет нулевую копию. Документация... разрежена, по меньшей мере.

В частности, splice работает только с нулевой копией, если страницы были даны как "подарок", т.е. вы больше не владеете ими (формально, но на самом деле вы все еще это делаете). Это не проблема, если вы просто соедините файловый дескриптор с сокетом, но это большая проблема, если вы хотите объединить данные из адресного пространства вашего приложения или из одного канала в другой. Непонятно, что делать со страницами после (и когда). В документации указано, что впоследствии вы не можете прикасаться к страницам или делать что-либо с ними, никогда, никогда. Поэтому, если вы следуете букве документации, вы должны пропустить память.
Это явно не правильно (это не может быть), но нет хорошего способа узнать (для вас хотя бы!), Когда он безопасен для повторного использования или выпуска этой памяти. Ядро, выполняющее sendfile, будет знать, поскольку, как только он получает TCP ACK, он знает, что данные больше никогда не нужны. Проблема в том, что вы никогда не увидите ACK. Все, что вы знаете, когда splice вернулось, - это то, что данные были приняты для отправки (но вы не знаете, было ли оно уже отправлено или получено, или когда это произойдет). Это означает, что вам нужно как-то понять это на уровне приложений, либо выполнив ручные ACK (поставляется бесплатно с надежным UDP), либо предположив, что если другая сторона отправит ответ на ваш запрос, они, очевидно, должны получить запрос.

Еще одна вещь, с которой вам нужно справиться, - это конечное пространство трубы. По умолчанию очень мало, но даже если вы увеличиваете размер, вы не можете просто наивно спланировать файл любого размера. sendfile с другой стороны просто позволит вам сделать это, что здорово.

В целом, sendfile хорош, потому что он просто работает, и он работает хорошо, и вам не нужно заботиться ни о какой из вышеперечисленных деталей. Это не панацея, но это, безусловно, отличное дополнение.
Я лично остался бы в стороне от splice и его семьи до тех пор, пока все это не будет пересмотрено и пока не станет на 100% ясно, что вы должны делать (и когда) и что вам не нужно делать.

Реальные, эффективные прибыли по сравнению с обычным старым write в любом случае являются маргинальными для большинства приложений. Я вспоминаю несколько менее вежливых комментариев г-на Торвальдса несколько лет назад (когда BSD имел форму write, которая сделала бы магию с переназначением страниц, чтобы получить нуль-копию, а Linux не сделал), в которой указывалось, что создание копия обычно не является проблемой, но играть с трюками со страницами не будет [здесь].

Ответ 2

Согласно соответствующей странице руководства по сращиванию с 2014-07-08, я цитирую:

Хотя мы говорим о копировании, фактических копий обычно избегают. Ядро делает это, внедряя буфер буфера как набор ссылок с подсчетом ссылок на страницы памяти ядра. Ядро создает "копии" страниц в буфере, создавая новые указатели (для выходного буфера), ссылаясь на страницы, и увеличивая количество ссылок для страниц: копируются только указатели, а не страницы буфера.

Следовательно, да, сращивание задокументировано в настоящее время нулевой копией в большинстве случаев.