Кратко

  • RFC 817 развёл архитектурный слой и исполняемый модуль. Внешние интерфейсы IP и TCP должны были оставаться совместимыми, но внутренняя граница могла проходить через оба протокола по функции демультиплексирования.
  • Изоляция облегчает замену компонентов, однако добавляет пакеты, копирование и переключения контекста. Узкая подсказка между слоями могла объединить ACK, окно и эхо Telnet, не передавая приложению полномочия TCP.
  • Оптимизацию разрешали данные о реальном пути: частый случай, измеренный узкий участок и локализованная специализация. Название структуры, красота схемы или редкое исключение не заменяли доказательство.

Две границы отвечали на разные вопросы

Стек «драйвер — IP — TCP — Telnet, FTP или SMTP» описывал услуги и отношения между протоколами. Он не говорил, должен ли таймер TCP работать в ядре, может ли обработчик ждать, какой процесс владеет соединением и сколько планирований потребуется входящему сегменту.

Если IP находился в ядре, а TCP целиком в отдельном процессе, адрес конечного пользовательского процесса нельзя было узнать до чтения заголовка TCP. Система сначала будила центральный TCP-процесс, тот выбирал соединение, после чего будилось приложение. Чистая линия между IP и TCP превратила одно решение о демультиплексировании в два перехода исполнения.

RFC 817 предложил функциональный разрез. В ядре оставалась достаточная часть IP и TCP, чтобы определить конечный процесс и передать датаграмму прямо ему. Остальная обработка, в том числе требующая таймеров или возможности блокироваться, продолжалась в среде этого процесса. Граница модуля пересекала два слоя, потому что следовала за местом дорогой функции.

Сетевой результат не менялся. Удалённая сторона видела тот же TCP и те же обязательства. Иным становилось только частное устройство хоста: размещение состояния, контекст выполнения, число пробуждений и перемещений данных.

Из этого следовала даже возможность иметь отдельный TCP-контекст на соединение и специализировать варианты для передачи файлов или интерактивной работы. Но каждую ошибку пришлось бы исправлять в нескольких местах. RFC 817 называл идею экспериментальной и не принятой повсеместно: локальная простота могла обернуться системным долгом сопровождения.

Процесс, ядро и внешний процессор переносили цену

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

Реализация в ядре экономила переключение процесса и хорошо стыковалась с устройствами. Но сложное действие по таймеру плохо чувствовало себя на уровне прерывания. Такой код обычно не мог блокироваться, мог надолго маскировать прерывания, а при потоке пакетов выше возможностей хоста — занять машину, не оставляя планировщику способа ограничить его. Дефицит памяти ядра и изменения ОС добавляли стоимость переноса.

Отдельный коммуникационный процессор обещал специализированную систему и повторное использование на нескольких хостах. Однако хосту всё равно требовался интерфейс с ним: форматирование, поток, события и отказы. Этот интерфейс сам был протоколом. Перенос стека в другую коробку создавал новую границу вместо исчезновения старой.

Ответственный выбор поэтому начинался не с лозунга «ядро или процесс», а с перечня действий: близость к устройству, демультиплексирование, ожидание, таймеры, состояние соединения, копирование, восстановление и цена каждого пересечения.

Один символ породил несколько правильных пакетов

В посимвольном Telnet входной знак создавал независимые обязанности. TCP должен был подтвердить приём. Приёмная сторона могла обновить окно. Telnet или приложение возвращали эхо; управляющий знак добавлял другие действия. Если каждый слой отправлял немедленно, на одно полезное событие приходилось несколько пакетов.

Никакой протокол не был нарушен. Соседние компоненты просто не знали о скором выходе данных друг у друга. С каждым пакетом повторялись прерывание, планирование и проход по трактам двух хостов; в тарифицируемой сети повторялась и денежная плата.

RFC 817 разрешил ограниченную координацию. TCP мог узнать, ожидается ли вскоре ответ верхнего слоя, и немного задержать ACK. Для интерактивного Telnet подтверждение, окно и эхо помещались в один сегмент. Для односторонней передачи файла та же задержка останавливала следующий пакет отправителя и могла снижать пропускную способность.

Значит, намерение верхнего слоя было не командой, а контекстом для конкретного класса трафика. RFC 1122 позднее ограничил delayed ACK половиной секунды и потребовал подтверждать как минимум каждый второй полноразмерный сегмент. Он повторил выигрыш «три сегмента в один» и одновременно предупредил о нарушении измерения RTT и пакетной синхронизации. TCP учитывал подсказку, но сохранял срок и власть над своей обязанностью.

Изоляция приносила пользу и выставляла счёт

Стабильный интерфейс позволяет изменить одну сторону, не заставляя другую знать её устройство. Несколько клиентов пользуются одной службой, разработчику не нужно держать в голове весь хост, а разные машины сохраняют совместимость. Благодаря этой изоляции семейство протоколов переживает конкретную реализацию.

Но фиксированный интерфейс скрывает намерение. Байтовый поток может заставить блочную передачу идти через неудобные операции. Процессы без общей памяти копируют данные через ядро. Независимые моменты отправки превращают совместимую информацию в отдельные пакеты.

RFC 817 называл границу слоя одновременно преимуществом и наказанием. Это не оправдывало произвольное сцепление. Счёт нужно было показать: пакеты, копии, пробуждения, поиски, задержка, число вариантов и стоимость исправления. Пересечение допустимо, только если сэкономленная работа измерена, раскрытая информация минимальна, а без оптимизации остаётся корректный стандартный путь.

RFC 1958 позднее поставил рядом модульность, производительность и стоимость и предпочёл обратную связь от работающих реализаций архитектурным максимам. RFC 3439 связал сложность с масштабированием и эксплуатационными расходами. Чистота не бесплатна, но и тесная связь сама по себе не эффективна.

Multics дал исключению числовой адрес

В Multics восьмибитные байты TCP-сегмента неудобно размещались в 36-битных словах. Ранняя процедура контрольной суммы тратила около шести миллисекунд на 576 байт. Тщательный, жёстко привязанный к машине код сократил время до менее одной миллисекунды.

RFC 817 прямо называл приём грязным. Он был приемлем не как общий стиль, а потому, что крайнее узкое место измерили, специализацию заперли в одной функции, а результат можно было сверить с эталонным путём. Локальная некрасивость получила границы и проверку.

Другие улучшения выглядели скромнее. Следующий сегмент обычно приходил по порядку, поэтому ожидаемый случай следовало проверять первым. Очередь повторной передачи чаще удаляла подтверждённые записи, чем отправляла их заново, и должна была ускорять удаление. Данные не стоило копировать только ради удобной границы модуля.

Время исчезало понемногу в контрольной сумме, копировании, поиске в очереди, планировании и смене состояний. Редко находился один монстр, после удаления которого стек становился быстрым. Производительность строилась вниманием ко всему пути до того, как карта модулей затвердеет.

Общий договор оставлял свободу частной реализации

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

RFC 817 вернул решению проверяемую основу: считать пакеты на полезное действие, отслеживать копии, отделять обычный трафик от исключений, записывать владельца межслойной подсказки, её срок и поведение при отсутствии. Затем охранять внешний договор, чтобы другая реализация могла выбрать иной внутренний разрез.

Слой был реален как обязательство перед другими. Его стоимость тоже была реальна. Модуль оставался местным решением и потому должен был доказать, зачем он пересекает стек и как это решение можно отменить.

Источники