Шрифт:
Закладка:
Илл. 6.53. (а) TCP-заголовок. (б) IP-заголовок. Серым цветом выделены поля, взятые из прототипа без изменений
Теперь рассмотрим быстрый путь обработки пакета получающей стороной на илл. 6.52. Первый шаг состоит в поиске записи соединения для входящего сегмента. В TCP запись соединения может храниться в хеш-таблице, ключом к которой является какая-нибудь простая функция двух IP-адресов и двух портов. После ее обнаружения следует проверить адреса и номера портов, чтобы убедиться, что найдена нужная запись.
Процесс поиска требуемой записи можно дополнительно ускорить, если установить указатель на последнюю использовавшуюся запись и сначала проверить ее. Кларк и его соавторы (Clark et al.,1989) исследовали этот вопрос и пришли к выводу, что в этом случае доля успешных обращений превысит 90 %.
Затем сегмент проверяется на соответствие стандарту: соединение в состоянии ESTABLISHED, ни одна сторона не пытается его разорвать, сегмент является полным, специальные флаги не установлены, и порядковый номер именно тот, который ожидался. Такие проверки включают всего несколько условий. Если все эти условия удовлетворяются, вызывается специальная процедура быстрого пути TCP-уровня.
Процедура быстрого пути обновляет запись соединения и копирует данные для пользователя. Во время копирования она подсчитывает контрольную сумму, что уменьшает количество циклов обработки. Если контрольная сумма верна, запись соединения обновляется и отправляется подтверждение. Метод, реализованный в виде отдельной процедуры, которая сначала быстро проверяет заголовок на предмет его соответствия ожиданиям, называется прогнозированием заголовков (header prediction). Он применяется в большинстве реализаций TCP. Использование этого метода оптимизации вместе с остальными, описанными в данном разделе, позволяет протоколу TCP достичь 90 % от скорости копирования в локальной памяти, при условии, что сама сеть достаточно быстрая.
Еще две области, в которых возможны существенные улучшения производительности, — управление буферами и таймерами. Как уже было сказано, в управлении буферами необходимо избегать излишнего копирования. В управлении таймерами следует учитывать, что они почти никогда не срабатывают. Они предназначены для обработки нестандартного случая потерь сегментов, но большинство сегментов и их подтверждений приходят успешно, и поэтому истечение периода ожидания является редким событием.
В программе таймеры обычно реализуются в виде связанного списка таймеров, отсортированного по времени срабатывания. Начальная запись содержит счетчик минимальных интервалов времени (тактовых импульсов — ticks), оставшихся до истечения периода ожидания. В каждой последующей записи находится счетчик, указывающий, сколько тактовых импульсов остается с учетом предыдущей записи. Например, если таймеры сработают через 3, 10 и 12 импульсов, счетчики списка будут содержать значения 3, 7 и 2 соответственно.
При каждом импульсе таймера счетчик начальной записи уменьшается на единицу. Когда он достигает нуля, обрабатывается связанное с этим таймером событие, а начальной записью становится следующий элемент списка. Значение счетчика можно оставить без изменений. В такой схеме добавление и удаление таймера требуют затрат ресурсов, при этом время выполнения операции пропорционально длине списка.
Если максимальное значение таймера ограничено и известно заранее, можно применить более эффективный метод — использовать массив под названием циклическое расписание (timing wheel) (илл. 6.54). Каждый слот соответствует одному тактовому импульсу. Текущее время, показанное на рисунке, — T = 4. Таймеры должны сработать через 3, 10 и 12 импульсов. Если новый таймер должен сработать через 7 импульсов, требуется сделать запись в слоте 11. Аналогично, если нужно отключить таймер, установленный на T + 10, нужно проверить список, начинающийся в слоте 14, и удалить соответствующую запись. Следует отметить, что массив на илл. 6.54 не может поддерживать таймеры за пределами T + 15.
Илл. 6.54. Циклическое расписание
Когда работает таймер, указатель текущего времени перемещается по циклическому расписанию вперед на один слот. Если запись, на которую он указывает, не нулевая, обрабатываются все таймеры этого списка. Многочисленные варианты этой концепции обсуждаются у Варгезе и Лаука (Varghese and Lauck, 1987).
6.7.7. Сжатие заголовков
Перейдем к вопросу о производительности беспроводных сетей и сетей других типов, где пропускная способность ограниченна. При уменьшении издержек, возникающих из-за программного обеспечения, портативные компьютеры будут работать эффективнее, но это не поможет улучшить производительность в тех случаях, когда проблемой являются сетевые каналы.
Чтобы эффективно использовать пропускную способность, заголовок протокола и пользовательские данные должны занимать как можно меньше битов. В случае пользовательских данных этого можно достичь путем компактного кодирования информации: к примеру, изображения лучше передавать в формате JPEG, а не в растровых форматах, а документы — в форматах со сжатием, таких как PDF. Также для этого необходимы механизмы кэширования прикладного уровня (например, кэш веб-страниц), уменьшающие число передач.
А что делать с заголовками протоколов? В беспроводных сетях на канальном уровне заголовки обычно занимают мало места, поскольку изначально рассчитаны на низкую пропускную способность. В сетях, ориентированных на установление соединения, пакеты имеют короткие идентификаторы соединения вместо длинных адресов. Но протоколы более высоких уровней (IP, TCP или UDP) универсальны для всех типов сетей, поэтому в них не используются компактные заголовки. На самом деле потоковая обработка, уменьшающая затраты на программное обеспечение, часто приводит к еще большему увеличению длины заголовка (например, в IPv6 используются более разреженные заголовки, чем в IPv4).
Заголовки более высоких уровней могут сильно влиять на производительность. Например, рассмотрим процесс передачи VoIP-данных через IP, UDP и RTP. Этим протоколам требуется заголовок длиной 40 байт (20 байт для IPv4, 8 — для UDP, 12 — для RTP). В случае IPv6 ситуация еще хуже: 60 байт, включая 40-байтный заголовок IPv6. Эти заголовки могут становиться еще длиннее, потребляя более половины пропускной способности.
Чтобы уменьшить пропускную способность, потребляемую заголовками высоких уровней, применяется сжатие заголовков (header compression). При этом используются не универсальные, а специально разработанные методы. Дело в том, что по отдельности заголовки сжимаются плохо (поскольку они и так короткие), а для декомпрессии требуется, чтобы исходные данные пришли на место назначения. Последнее может и не произойти из-за потери пакетов.
Сжатие заголовков можно эффективно реализовать, если учитывать формат протокола. Одна из первых схем была разработана Ван Джейкобсоном (1990) и применялась для сжатия TCP/IP-заголовков при передаче по медленным последовательным каналам. Она позволяет уменьшить обычный 40-байтный TCP/IP-заголовок в среднем до 3 байт. Если посмотреть на илл. 6.53, можно догадаться, на чем основан этот метод. Дело в том, что многие поля заголовка не меняются от пакета к пакету. К примеру, совсем не