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;
    }
};
Другие вопросы по тегам