Vmsplice() и TCP
В исходной реализации vmsplice()
было предложено, чтобы, если у вас был буфер пользователя на уровне пользователя 2x максимальное количество страниц, которые могли бы вставляется в трубу, удаленная vmsplice() во второй половине буфера гарантирует, что ядро было выполнено с использованием первой половины буфера.
Но в конце концов это было не так, и особенно для TCP, страницы ядра сохранялись до получения ACK с другой стороны. Исправление этого было оставлено как будущая работа, и, следовательно, для TCP, ядру все равно пришлось бы копировать страницы из канала.
vmsplice()
имеет параметр SPLICE_F_GIFT
, который имеет отношение к этому, но проблема в том, что в этом есть две другие проблемы - как эффективно получать свежие страницы из ядра и как уменьшить кеш-хаус. Первая проблема заключается в том, что mmap требует, чтобы ядро очистило страницы, а вторая проблема заключается в том, что хотя mmap может использовать функцию fa kscrubd в ядре, что увеличивает рабочий набор процесса (кэширование).
Исходя из этого, у меня есть следующие вопросы:
- Каково текущее состояние уведомления пользователей о безопасном повторном использовании страниц? Меня особенно интересуют страницы splice() d на сокет (TCP). Что-нибудь случилось за последние 5 лет?
- Является ли
mmap
/vmsplice
/splice
/munmap
текущей лучшей практикой для нулевого копирования на сервере TCP или у нас есть лучшие варианты сегодня?
Ответы
Ответ 1
Да, из-за того, что сокет TCP удерживается на страницах в течение неопределенного времени, вы не можете использовать схему двойной буферизации, упомянутую в примере кода. Кроме того, в моем примере использования страницы поступают из кругового буфера, поэтому я не могу подарить страницы ядру и выделять свежие страницы. Я могу проверить, что я вижу повреждение данных в полученных данных.
Я прибегал к опросу уровня очереди отправки сокета TCP до тех пор, пока он не дренируется до 0. Это исправляет повреждение данных, но является субоптимальным, поскольку удаление очереди отправки на 0 влияет на пропускную способность.
n = ::vmsplice(mVmsplicePipe.fd.w, &iov, 1, 0);
while (n) {
// splice pipe to socket
m = ::splice(mVmsplicePipe.fd.r, NULL, mFd, NULL, n, 0);
n -= m;
}
while(1) {
int outsize=0;
int result;
usleep(20000);
result = ::ioctl(mFd, SIOCOUTQ, &outsize);
if (result == 0) {
LOG_NOISE("outsize %d", outsize);
} else {
LOG_ERR_PERROR("SIOCOUTQ");
break;
}
//if (outsize <= (bufLen >> 1)) {
if (outsize == 0) {
LOG("outsize %d <= %u", outsize, bufLen>>1);
break;
}
};