vmsplice () и TCP - PullRequest
       30

vmsplice () и TCP

20 голосов
/ 21 июня 2011

В исходной реализации vmsplice(), было предложено , что если у вас есть буфер пользовательской земли, в 2 раза превышающий максимальное количество страниц, которое может поместиться в канале, успешный vmsplice () на втором половина буфера будет гарантировать, что ядро ​​выполнено с использованием первой половины буфера.

Но в конце концов это не так, и особенно для TCP страницы ядра будут сохраняться до получения ACK с другой стороны. Исправление этого было оставлено в качестве будущей работы, и поэтому для TCP ядру все равно пришлось бы копировать страницы из конвейера.

vmsplice() имеет опцию SPLICE_F_GIFT, которая вроде бы решает эту проблему, но проблема в том, что это открывает две другие проблемы - как эффективно получать свежие страницы из ядра и как уменьшить кэш-память Первая проблема заключается в том, что mmap требует, чтобы ядро ​​очистило страницы, а вторая проблема заключается в том, что хотя mmap может использовать в ядре необычную функцию kscrubd , которая увеличивает рабочий набор процесса (очистка кэша) .

Исходя из этого, у меня есть следующие вопросы:

  • Каково текущее состояние для уведомления пользователей о безопасном повторном использовании страниц? Меня особенно интересуют страницы splice () d на сокете (TCP). Что-нибудь случилось за последние 5 лет?
  • Является ли mmap / vmsplice / splice / munmap наилучшей практикой для нулевого копирования на TCP-сервере или у нас есть лучшие варианты сегодня?

1 Ответ

5 голосов
/ 22 июля 2011

Да, из-за того, что сокет 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;
    }
};
...