vmsplice() и TCP
В оригинале vmsplice()
В ходе реализации было предложено, что если у вас есть пользовательский буфер, в 2 раза превышающий максимальное количество страниц, которое может поместиться в конвейере, успешное выполнение vmsplice() во второй половине буфера гарантирует, что ядро выполнено с использованием первой половины. буфера.
Но в конце концов это было не так, и особенно для TCP, страницы ядра сохранялись до получения ACK с другой стороны. Исправление этого было оставлено в качестве будущей работы, и, таким образом, для TCP ядру все равно пришлось бы копировать страницы из конвейера.
vmsplice()
имеет SPLICE_F_GIFT
Эта опция имеет дело с этим, но проблема в том, что это выставляет две другие проблемы - как эффективно получать свежие страницы из ядра, и как уменьшить кэш-память. Первая проблема заключается в том, что mmap требует, чтобы ядро очистило страницы, а вторая проблема заключается в том, что, хотя mmap может использовать в ядре необычную функцию 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;
}
};