Что нужно знать о нулевой копии

распределенный

0x01 Введение: традиционный режим копирования

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

for (;;) {
    if (lseek(fd, 0, SEEK_SET) < 0) err_quit("error seek file");

    connect_fd = accept(listen_fd, &serv_addr, &client_addr);
    if (connect_fd < 0) err_quit("accept failed");

    int n = 0;
    char buf[BUFFER_SIZE];
    while ((n = read(fd, buf, BUFFER_SIZE)) > 0) {
        write(connect_fd, buf, n);
    }
    close(connect_fd);
}

Для многих приложений такое циклическое чтение и запись приемлемо, но если нам нужно часто передавать большие файлы через сокет, этот метод очень неэффективен: этот процесс включает4 копии данных,Как показано ниже

-w825

Подробная временная диаграмма выглядит следующим образом:

  1. Выполнить системный вызов чтения
  2. Контекст операционной системы переключается в режим ядра, а DMA считывает содержимое файла с диска и сохраняет его в буферном кеше ядра (1-й переключатель контекста, 1-я копия данных)
  3. Ядро операционной системы копирует данные из ядра в буфер пользовательского пространства, контекст переключается в пользовательский режим, и функция read() завершает работу (второе переключение контекста, вторая копия данных).
  4. Выполните некоторый возможный бизнес-код, затем выполните системный вызов записи
  5. Контекст операционной системы переключается в режим ядра, а затем данные копируются из пространства пользователя в буфер отправки сокета в пространстве ядра (3-й переключатель контекста, 3-я копия данных)
  6. Функция записи возвращается, операционная система переходит в пользовательский режим, и в то же время происходит четвертое копирование, DMA копирует данные из буфера ядра в механизм обработки сетевого протокола сетевой карты (четвертое переключение контекста, четвертое копирование данных )

Видно, что задействованы две копии между пространством пользователя и пространством ядра, одна используется для копирования содержимого файла из кеша буфера ядра в пространство пользователя, а другая используется для копирования буфера пространства пользователя в пространство ядра. так как это может быть передано только через сокеты.Если приложению вообще не нужно ничего делать с содержимым файла перед тем, как инициировать передачу, то такое копирование туда-сюда совершенно бесполезно.

0x02 лучше

Начиная с версии ядра 2.1, в Linux для упрощения операции введен sendfile.Метод sendfile успешно уменьшает количество копий данных с четырех до трех (только одна из которых использует ЦП), но этот метод еще не достиг нулевого копирования. требование. Основной код выглядит следующим образом

for (;;) {
    connect_fd = accept(listen_fd, &serv_addr, &client_addr);
    if (connect_fd < 0) err_quit("accept failed");

    struct stat stat_buf;
    fstat(fd, &stat_buf);
    off_t offset = 0;
    int cnt = 0;
    if ((cnt = sendfile(connect_fd, fd, &offset, stat_buf.st_size)) < 0) {
        err_quit("send file failed");
    }
    close(connect_fd);
}

-w750

Конкретная блок-схема выглядит следующим образом

  1. Выполнить системный вызов sendfile
  2. DMA считывает содержимое файла с диска и сохраняет его в буферном кеше ядра.
  3. Скопируйте данные из буфера ядра в буфер отправки сокета.
  4. Механизм DMA передает данные из буфера отправки сокета в механизм протокола.

0x03 Настоящая нулевая копия

Когда сетевая карта поддерживаетscatter-n-gatherрежиме, вы также можете удалить среднюю копию в 0x02 Конкретный процесс показан на следующем рисунке:

-w747

Конкретная блок-схема выглядит следующим образом

На данный момент копия данных не имеет ничего общего с ЦП, она полностью управляется ядром, а ядру нужны только две копии.

0x04 Тестовый код

Репозиторий кода выглядит следующим образом:GitHub.com/Артур-Станция…Моделирование представляет собой сервер статических ресурсов.Когда клиент подключается, он возвращает содержимое файла статических ресурсов index.html. Способ проверки заключается в использованииtelnet localhost 34567Имитация клиентского подключения