У Linux есть нулевая копия? склеить или отправить файл?
Когда была введена splice, в списке ядра обсуждалось, что sendfile был повторно реализован на основе splice. Документация по соединению SLICE_F_MOVE гласит:
Попытайтесь переместить страницы вместо копирования. Это только подсказка ядру: страницы все равно могут быть скопированы, если ядро не может переместить страницы из канала или если буферы канала не ссылаются на полные страницы. Первоначальная реализация этого флага была ошибочной: поэтому, начиная с Linux 2.6.21, он не работает (но все еще разрешен в вызове splice()); в будущем правильная реализация может быть восстановлена.
Значит ли это, что в Linux нет метода нулевого копирования для записи в сокеты? Или это было исправлено в какой-то момент, и никто не обновлял документацию годами? Есть ли в sendfile или splice реализация нулевой копии в любой из последних версий ядра 3.x?
Поскольку у Google нет ответа на этот запрос, я создаю вопрос о переполнении стека для следующего бедняги, который хочет знать, есть ли какая-либо польза от использования vmsplice и splice или sendfile по сравнению с простой старой записью.
2 ответа
sendfile
с тех пор и до сих пор является нулевой копией (при условии, что аппаратное обеспечение это позволяет, но обычно это так). Быть ноль-копией было главной целью иметь этот системный вызов в первую очередь. sendfile
в настоящее время реализован в качестве оболочки вокруг splice
,
Это говорит о том, что splice
тоже является нулевой копией, и это действительно так. По крайней мере, в теории, и, по крайней мере, в некоторых случаях. Проблема состоит в том, чтобы выяснить, как правильно использовать его, чтобы он работал надежно и чтобы он был без копий. Документация... скудная, если не сказать больше.
Особенно, splice
работает только в нулевом экземпляре, если страницы были предоставлены в качестве "подарка", т. е. у вас их больше нет (формально, но на самом деле вы все еще ими владеете). Это не проблема, если вы просто объединяете файловый дескриптор в сокет, но это большая проблема, если вы хотите объединить данные из адресного пространства вашего приложения или из одного канала в другой. Неясно, что делать со страницами впоследствии (и когда). В документации говорится, что вы не можете впоследствии трогать страницы или делать с ними что-либо, никогда, никогда. Поэтому, если вы следуете букве документации, вы должны утечь память.
Это явно не правильно (это не может быть), но нет хорошего способа узнать (по крайней мере, для вас!), Когда безопасно повторно использовать или освободить эту память. Ядро делает sendfile
будет знать, так как, как только он получает ACK TCP, он знает, что данные больше не нужны. Проблема в том, что вы никогда не увидите ACK. Все, что вы знаете, когда splice
возвращается, что данные были приняты для отправки (но вы не знаете, были ли они уже отправлены или получены, и когда это произойдет).
Это означает, что вам нужно как-то выяснить это на прикладном уровне, либо выполнив ручные ACK (поставляется бесплатно с надежным UDP), либо предположив, что, если другая сторона отправит ответ на ваш запрос, они, очевидно, должны были получить запрос.,
Еще одна вещь, которой вы должны управлять - это ограниченное пространство трубы. Значение по умолчанию очень мало, но даже если вы увеличите размер, вы не сможете просто наивно склеить файл любого размера. sendfile
с другой стороны, просто позволит вам сделать это, что круто.
В общем, sendfile
это хорошо, потому что это просто работает, и это работает хорошо, и вам не нужно заботиться о любой из вышеперечисленных деталей. Это не панацея, но это, безусловно, отличное дополнение.
Я бы лично держался подальше от splice
и его семья до тех пор, пока все это не будет полностью пересмотрено и пока не станет на 100% ясно, что вы должны делать (и когда), а что нет.
Реальный, эффективный выигрыш над простым старым write
в любом случае, являются маргинальными для большинства приложений. Я вспоминаю несколько менее вежливые комментарии г-на Торвальдса несколько лет назад (когда у BSD была форма write
это сделало бы какое-то волшебство с переназначением страниц, чтобы получить нулевое копирование, а Linux этого не сделал), что указывало, что создание копии обычно не представляет никакой проблемы, но игра трюками со страницами - это [не повторю здесь].
Согласно соответствующей справочной странице по соединению от 2014-07-08 я цитирую:
Хотя мы говорим о копировании, реальных копий обычно избегают. Ядро делает это путем реализации конвейерного буфера как набора указателей с подсчетом ссылок на страницы памяти ядра. Ядро создает "копии" страниц в буфере, создавая новые указатели (для выходного буфера), ссылающиеся на страницы, и увеличивая количество ссылок для страниц: копируются только указатели, а не страницы буфера.
Поэтому, да, в большинстве случаев сращивание задокументировано как нулевая копия.