Ответ 1
К сожалению, вы не можете использовать sendfile()
здесь, потому что назначение не является сокетом. (Имя sendfile()
происходит от send()
+ "файла" ).
Для нулевой копии вы можете использовать splice()
, как было предложено @Dave. (За исключением того, что он не будет нулевой копией, он будет "одной копией" из кеша страницы исходного файла в кеш страницы страницы целевого файла.)
Однако... (a) splice()
зависит от Linux; и (б) вы почти наверняка можете сделать так же, используя портативные интерфейсы, если вы используете их правильно.
Короче говоря, используйте open()
+ read()
+ write()
с небольшим временным буфером. Я предлагаю 8K. Таким образом, ваш код будет выглядеть примерно так:
int in_fd = open("source", O_RDONLY);
assert(in_fd >= 0);
int out_fd = open("dest", O_WRONLY);
assert(out_fd >= 0);
char buf[8192];
while (1) {
ssize_t result = read(in_fd, &buf[0], sizeof(buf));
if (!result) break;
assert(result > 0);
assert(write(out_fd, &buf[0], result) == result);
}
С помощью этого цикла вы будете копировать 8K из кеша страницы in_fd в кеш процессора L1, а затем записывать его из кеша L1 в кеш страницы out_fd. Затем вы перезапишете эту часть кеша L1 следующим блоком 8K из файла и так далее. Конечным результатом является то, что данные в buf
никогда вообще не будут храниться в основной памяти (за исключением, может быть, один раз в конце); с точки зрения ОЗУ системы, это так же хорошо, как использование "нулевой копии" splice()
. Кроме того, он отлично переносится в любую систему POSIX.
Обратите внимание, что здесь используется небольшой буфер. Типичные современные процессоры имеют 32K или около того для кэша данных L1, поэтому, если вы сделаете буфер слишком большим, этот подход будет медленнее. Возможно много, гораздо медленнее. Поэтому держите буфер в диапазоне "несколько килобайт".
Конечно, если ваша дисковая подсистема очень быстрая, пропускная способность памяти, вероятно, не является вашим ограничивающим фактором. Поэтому я бы рекомендовал posix_fadvise
, чтобы ядро узнало, что вы делаете:
posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);
Это даст подсказку ядру Linux о том, что его механизм с открытым исходным кодом должен быть очень агрессивным.
Я также предложил бы использовать posix_fallocate
для предопределения хранилища для целевого файла. Это покажет вам, будет ли у вас закончиться диск. А для современного ядра с современной файловой системой (например, XFS) это поможет уменьшить фрагментацию в целевом файле.
Последнее, что я бы рекомендовал, - mmap
. Это, как правило, самый медленный подход во всем, благодаря избиению TLB. (Очень свежие ядра с "прозрачными огромными страницами" могли бы смягчить это: я не пробовал в последнее время, но он, конечно, был очень плохим. Поэтому я бы только испытал тестирование mmap
, если у вас есть много времени для тестирования и очень недавнего ядра.)
[Обновление]
В комментариях есть вопрос о том, является ли splice
из одного файла в другой нулевой копией. Разработчики ядра Linux называют эту "кражу страницы". Как справочная страница для splice
, так и комментариев в источнике ядра говорят, что флаг SPLICE_F_MOVE
должен обеспечивать эту функциональность.
К сожалению, поддержка SPLICE_F_MOVE
была yanked в 2.6.21 (назад в 2007) и никогда не заменялась. (Комментарии в источниках ядра никогда не обновлялись.) Если вы ищите исходные файлы ядра, вы обнаружите, что SPLICE_F_MOVE
на самом деле не ссылается нигде. последнее сообщение, которое я могу найти (с 2008 года), говорит, что оно "ждет замены".
В нижней строке указано, что splice
из одного файла в другой вызывает memcpy
для перемещения данных; это не нуль-копия. Это не намного лучше, чем вы можете сделать в пользовательском пространстве с помощью read
/write
с небольшими буферами, поэтому вы можете также придерживаться стандартных портативных интерфейсов.
Если "кража страницы" когда-либо добавляется обратно в ядро Linux, преимущества splice
будут намного больше. (И даже сегодня, когда пункт назначения является сокетом, вы получаете истинную нулевую копию, делая splice
более привлекательным.) Но для целей этого вопроса splice
не покупает вас очень много.