Tc linux ������ �������� ������ ����������� �������

Новичкам про управление шириной канала в Linux

Некоторое время назад меня попросили настроить в удаленном филиале простейшую балансировку трафика. Работают они, бедолаги, через ADSL, и отправка электронных писем большого объема (сканы документов) забивает им весь обратный канал, что приводит к проблемам в работе с офисными онлайн-программами через VPN.
В качестве шлюза у них используется Linux (Fedora). До этого я пару раз видел, как подобная балансировка настраивается через ipfw на FreeBSD, а так как знаю механизм iptables достаточно хорошо, не ожидал особых проблем. Но поискав в Интернете, я был неприятно удивлен тем, что iptables мне тут совсем не помощник. И знания о порядке прохождения пакетов через его таблицы и правила мне почти не пригодятся. Нужно изучать tc из пакета iproute2.

Неожиданно для себя, я потратил два дня, для того чтобы более-менее разобраться в балансировке трафика средствами iproute2. Сначала попалась не самая лучшая для новичка статья про HTB(здесь). Различные примеры из Интернет тоже порой вводили в ступор, так как в них часто не было описания конкретных опций или смысла их применения. Поэтому я и попытался собрать полученные мною знания в одну статью, а главное описать все на доступном для новичков уровне.

Сразу оговорюсь, “резать” будем только исходящий с сетевого интерфейса трафик. Входящий тоже можно регулировать, но это требует дополнительных хитростей.

Бесклассовые дисциплины

Итак, в Linux для управления трафиком каждому сетевому интерфейсу назначается дисциплина (qdisc). Именно из дисциплин и строится вся система управления трафиком. Но пугаться не стоит, на самом деле, дисциплина — это просто алгоритм обработки очереди сетевых пакетов.
Дисциплин на одном интерфейсе может быть задействовано несколько, а непосредственно к интерфейсу крепится так называемая корневая дисциплина (root qdisc). При этом каждый интерфейс имеет свою собственную корневую дисциплину.

prio_fast

По-умолчанию после загрузки системы, root qdisc задает алгоритм обработки пакетов типа pfifo_fast.

qdisc pfifo_fast 0: dev eth0 root bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

pfifo_fast — это обычный алгоритм “First Input — First Output”, но с некоторой приоритизацией трафика. Дисциплина такого типа содержит внутри себя три очереди FIFO с разным приоритетом обработки пакетов. Пакеты раскладываются по ним на основе флага ToS (Type of Service) в каждом IP-пакете. Пакет попавший в FIFO0 имеет наивысший приоритет к обработке, в FIFO2 — наименьший. Сам ToS требует отдельного разговора, поэтому предлагаю ограничиться тем фактом, что операционная система сама знает какой ToS назначить отправленному IP-пакету. Например, в пакетах telnet и ping ToS будет иметь разные значения.

0: — дескриптор корневой дисциплины.
Дескрипторы должны иметь вид старший_номер: младший_номер, но у дисциплин младший номер всегда должен быть 0, и поэтому его можно опускать.

Параметр priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1, как раз задает побитовое соответствие поля ТоS каждой внутренней очереди (band). Например, при ToS=4, пакет обрабатывается в очереди 1, при ToS=7 в очереди 0.

В ряде источников указано, что параметры дисциплины pfifo_fast нельзя изменить, поверим.

Теперь рассмотрим как можно ограничить скорость общего исходящего трафика. Для этого назначим корневой дисциплиной интерфейса дисциплину типа TBF (Token Bucket Filter).

# tc qdisc add dev eth0 root tbf rate 180kbit latency 20ms buffer 1540

rate 180kbit — устанавливает порог скорости передачи на интерфейсе.

latency 20ms — задает максимальное время нахождения пакета данных в ожидании токена.

buffer 1540 — задаем размер буфера токенов в байтах. В примерах пишут, что для ограничения в 10Мbit/s достаточно буфера на 10Kbytes. Главное не сделать его слишком малого размера, больше можно. Примерная формула расчета: rate_in_Bytes/100.

Дисциплина ТBF для своей работы использует механизм токенов. Токены генерируются системой с постоянной скоростью и помещаются в буфер(bucket). За каждый токен, вышедший из буфера с интерфейса уходит IP-пакет.
Если скорости передачи пакетов и генерации токенов совпадает, процесс передачи данных идет без задержки.
Если скорость передачи пакетов меньше чем скорость токенов, последние начинают накапливаться в буфере и затем могут использоваться для кратковременной передачи данных на скорости выше пороговой.
Если скорость передачи пакетов выше — токенов начинает не хватать. Пакеты данных ожидают новых токенов некоторое время, а затем начинают отбрасываться.

Описанные две дисциплины относятся к так называемым бесклассовым (classless) дисциплинам. Они имеют ряд функциональных ограничений: подключаются только к интерфейсу (либо краевому классу), плюс для них нельзя применять фильтры пакетов. И соответственно мою задачу по балансировке почтового трафика с их помощью решить не удастся.
Кстати, полный набор бесклассовых дисциплин несколько шире: pfifo, bfifo, sqf (обеспечивает одинаковую скорость пакетов поступивших из разных потоков), esqf и т.д.

Классовые дисциплины

Если дисциплины можно представить как отрезки водопроводных труб, то классы — это соединители (фитинги). Это может быть простой фитинг-переходник, а может хитрый фитинг-разветвитель с десятком отводов.

В качестве родителя класса может выступать либо дисциплина, либо другой класс.
К классу могут присоединяться дочерние классы (состыковка нескольких фитингов).

Класс, не имеющий дочерних классов, называется краевым (leaf class). Здесь пакеты данных, пробежав по нашему “водопроводу” покидают систему управления трафиком и отправляются сетевым интерфейсом. По-умолчанию, любой краевой класс имеет присоединенную дисциплину типа fifo, и именно она определяет порядок передачи пакетов для этого класса. Но вся прелесть в том, что мы можем поменять эту дисциплину на любую другую.
В случае добавления дочернего класса данная дисциплина удаляется.

Читайте также:  Password account microsoft windows phone

Вернемся к задаче по балансировке почтового трафика и рассмотрим классовую дисциплину prio.
Она очень похожа на уже описанную pfifo_fast. Но данная дисциплина особенная тем, что при ее назначении автоматически создается три класса (количество можно менять параметром bands).

Заменим корневую дисциплину интерфейса на prio.

# tc qdisc add dev eth0 root handle 1: prio

handle 1: — задаем дескриптор данной root qdisc. В классовых дисциплинах его затем указывают при подключении классов.

qdisc prio 1: dev eth0 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1

# tc -d -s class show dev eth0

class prio 1:1 parent 1:
Sent 734914 bytes 7875 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
class prio 1:2 parent 1:
Sent 1555058583 bytes 8280199 pkt (dropped 124919, overlimits 26443 requeues 0)
backlog 0b 0p requeues 0
class prio 1:3 parent 1:
Sent 57934378 bytes 802213 pkt (dropped 70976, overlimits 284608 requeues 0)
backlog 0b 0p requeues 0

Видим три класса с идентификаторами 1:1, 1:2 и 1:3, подключенные к родительской диcциплине 1: типа prio (классы обязаны иметь общий старший_номер идентификатора со своим родителем).
Т.е на “трубу” root qdisc, которая разделяет потоки данных также как это делает pfifo_fast мы насадили тройник. Руководствуясь ToS, в класс 1:1 попадает высокоприоритетный трафик, в класс 1:2 обычный трафик, в класс 1:3 совсем уж «мусор».

Допустим обратный канал ADSL выдает скорость 90Кбайт/c. Разделим его на 20Kбайт/c под почту и 70Kбайт/c на все остальное.

Трафик из класса 1:1 специально ограничивать не будем. Его пакеты всегда смогут занять хоть всю ширину канала из-за высокого приоритета ToS, но объем трафика в этом классе у нас будет ничтожно мал по сравнению с остальными двумя классами. Поэтому отдельную полосу под него не резервируем.

Стандартный трафик, как правило, попадает в класс 1:2. Подключаем ко второму выводу класса-тройника трубу-дисциплину на 70Kбайт/c:

# tc qdisc add dev eth0 parent 1:2 handle 10: tfb rate 70kbps buffer 1500 latency 50ms

На третий вывод тройника подключим трубу-дисциплину на 20Kбайт/c:

# tc qdisc add dev eth0 parent 1:3 handle 20: tfb rate 20kbps buffer 1500 latency 50ms

Все три этих класса краевые.

И теперь осталось только направить почтовый трафик не в класс 1:2, как происходило раньше, а в класс 1:3. Это делается с помощью фильтров классовых дисциплин.

# tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:3

parent 1: — фильтр может крепиться только к дисциплине и вызывается из нее же. На основе срабатывания фильтра дисциплина решает в каком классе продолжится обработка пакета.

protocol ip — определяем тип сетевого протокола

prio 1 — параметр долго вводил меня в замешательство, так как он применяется в классах и фильтрах, плюс это название дисциплины. Здесь prio задает приоритет срабатывания фильтров, первыми задействуются фильтры с меньшим prio.

u32 — так называемый классификатор трафика, который может выполнять отбор пакетов по любому его признаку: по ip-адресу отправителя/получателя, по порту источника/приeмника, по типу протокола. Эти условия, собственно, и указаны далее.

match ip dport 25 0xffff — задает срабатывание фильтра при отправке пакетов на порт 25. 0xffff — это битовая маска для номера порта.

flowid 1:3 — указываем в какой класс передаются пакеты при срабатывании данного фильтра.

Сделано грубо, но задачу выполнит.

Смотрим статистику прохождения пакетов:

# tc -s -d qdisq show dev eth0
# tc -s -d class show dev eth0
# tc -s -d filter show dev eth0

Быстро удалить все классы, фильтры и вернуть root qdisc интерфейса в первоначальное состояние можно командой:

# tc qdisc del dev eth0 root

С другой стороны, у нас и так слишком тонкий обратный канал, чтобы резервировать 20Kбайт/c только под отправку электронной почты. Поэтому здесь лучше использовать классовую дисциплину HTB (Hierarchical Token Bucket). Она позволяет производить заимствование полосы пропускания дочерним классами у родительского.

# tc qdics add dev eth0 root handle 1: htb default 20

default 20 — задаем класс по-умолчанию. В нем будут обрабатываться пакеты, не попавшие в другие классы дисциплины htb. Если его не указать, то будет назначен “default 0” и весь неклассифицированный (непопавший под фильтры) трафик будет отправляться со скоростью интерфейса.

# tc class add dev eth0 parent 1: classid 1:1 htb rate 90kbps ceil 90kbps

прикрепляем к root qdisc класс с идентификатором 1:1. Тем самым ограничиваем скорость на интерфейсе до 90Кбайт/c.

classid 1:1 — идентификатор класса.

rate 90kbps — устанавливаем нижний порог пропускной способности для класса.

ceil 90kbps — устанавливаем верхний порог пропускной способности для класса.

# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 20kbps ceil 70kbps

создаем класс 1:10, дочерний классу 1:1. Затем в него фильтром будет направляться исходящий почтовый трафик.

rate 20kbps — устанавливаем гарантированный нижний порог пропускной способности для класса.

ceil 70kbps — устанавливаем верхний порог пропускной способности для класса. В случае если у родительского класса будет свободна полоса пропускания (наличие “лишних” токенов), class 1:10 сможет временно поднять скорость передачи данных, вплоть до указанного предела в 70Кбайт/c.

Читайте также:  Mojave mac os �������������� ����������

# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 70kbps ceil 90kbps

Создаем класс по умолчанию. В него будет попадать весь остальной трафик. Точно также, параметрами rate и ceil, задаем расширение пропускной способности в случае отсутствия уже почтового трафика.

# tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip dport 25 0xffff flowid 1:10

фильтр на базе u32, направляющий пакеты исходящие на 25й порт в класс 1:10.

Кстати, в документации указано, что по факту в HTB шейпинг трафика происходит только в краевых классах, в нашем случае 1:10 и 1:20. Указание параметров ограничения полосы пропускания в остальных классах HTB нужно лишь для функционирования системы заимствования между классами.

При добавлении класса также возможно указать параметр prio. Он задает приоритет класса (0 — макс.приоритет). Классы с меньшим приоритетом не обрабатываются пока есть данные в более приоритетных классах.

Источник

Символы Unicode: о чём должен знать каждый разработчик

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

Введение в кодировку

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

Краткая история кодировки

На заре своего развития интернет был исключительно англоязычным. Его авторам и пользователям не нужно было заботиться о символах других языков, и все нужды полностью покрывала кодировка American Standard Code for Information Interchange (ASCII).

ASCII — это таблица сопоставления бинарных обозначений знакам алфавита. Когда компьютер получает такую запись:

то с помощью ASCII он преобразует её во фразу «Hello world».

Один байт (восемь бит) был достаточно велик, чтобы вместить в себя любую англоязычную букву, как и управляющие символы, часть из которых использовалась телепринтерами, так что в те годы они были полезны (сегодня уже не особо). К управляющим символам относился, например 7 (0111 в двоичном представлении), который заставлял компьютер издавать сигнал; 8 (1000 в двоичном представлении) — выводил последний напечатанный символ; или 12 (1100 в двоичном представлении) — стирал весь написанный на видеотерминале текст.

В те времена компьютеры считали 8 бит за один байт (так было не всегда), так что проблем не возникало. Мы могли хранить все управляющие символы, все числа и англоязычные буквы, и даже ещё оставалось место, поскольку один байт может кодировать 255 символов, а для ASCII нужно только 127. То есть неиспользованными оставалось ещё 128 позиций в кодировке.

Вот как выглядит таблица ASCII. Двоичными числами кодируются все строчные и прописные буквы от A до Z и числа от 0 до 9. Первые 32 позиции отведены для непечатаемых управляющих символов.

Проблемы с ASCII

Позиции со 128 по 255 были пустыми. Общественность задумалась, чем их заполнить. Но у всех были разные идеи. Американский национальный институт стандартов (American National Standards Institute, ANSI) формулирует стандарты для разных отраслей. Там утвердили позиции ASCII с 0 по 127. Их никто не оспаривал. Проблема была с остальными позициями.

Вот чем были заполнены позиции 128-255 в первых компьютерах IBM:

Какие-то загогулины, фоновые иконки, математические операторы и символы с диакретическим знаком вроде é. Но разработчики других компьютерных архитектур не поддержали инициативу. Всем хотелось внедрить свою собственную кодировку во второй половине ASCII.

Все эти различные концовки назвали кодовыми страницами.

Что такое кодовые страницы ASCII?

Здесь собрана коллекция из более чем 465 разных кодовых страниц! Существовали разные страницы даже в рамках какого-то одного языка, например, для греческого и китайского. Как можно было стандартизировать этот бардак? Или хотя бы заставить его работать между разными языками? Или между разными кодовыми страницами для одного языка? В языках, отличающихся от английского? У китайцев больше 100 000 иероглифов. ASCII даже не может всех их вместить, даже если бы решили отдать все пустые позиции под китайские символы.

Эта проблема даже получила название Mojibake (бнопня, кракозябры). Так говорят про искажённый текст, который получается при использовании некорректной кодировки. В переводе с японского mojibake означает «преобразование символов».

Пример бнопни (кракозябров).

Безумие какое-то.

Именно! Не было ни единого шанса надёжно преобразовывать данные. Интернет — это лишь монструозное соединение компьютеров по всему миру. Представьте, что все страны решили использовать собственные стандарты. Например, греческие компьютеры принимают только греческий язык, а английские отправляют только английский. Это как кричать в пустой пещере, тебя никто не услышит.

ASCII уже не удовлетворял жизненным требованиям. Для всемирного интернета нужно было создать что-то другое, либо пришлось бы иметь дело с сотнями кодовых страниц.

��� Если только ������ вы не хотели ��� бы ��� читать подобные параграфы. �֎֏0590֐��׀ׁׂ׃ׅׄ׆ׇ

Так появился Unicode

Unicode расшифровывают как Universal Coded Character Set (UCS), и у него есть официальное обозначение ISO/IEC 10646. Но обычно все используют название Unicode.

Читайте также:  Windows cut from screen

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

Кто-то проделал огромную работу, сопоставляя каждый символ во всех языках с уникальными кодами. Вот как это выглядит:

Префикс U+ говорит о том, что это стандарт Unicode, а число — это результат преобразования двоичных чисел. Стандарт использует шестнадцатеричную нотацию, которая является упрощённым представлением двоичных чисел. Здесь вы можете ввести в поле что угодно и посмотреть, как это будет преобразовано в Unicode. А здесь можно полюбоваться на все 143 859 кодовых пунктов.

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

Осталось добавить последний ингредиент.

Unicode Transform Protocol (UTF)

UTF — протокол кодирования кодовых пунктов в Unicode. Он прописан в стандарте и позволяет кодировать любой кодовый пункт. Однако существуют разные типы UTF. Они различаются количеством байтов, используемых для кодировки одного пункта. В UTF-8 используется один байт на пункт, в UTF-16 — два байта, в UTF-32 — четыре байта.

Но если у нас есть три разные кодировки, то как узнать, какая из них применяется в конкретном файле? Для этого используют маркер последовательности байтов (Byte Order Mark, BOM), который ещё называют сигнатурой кодировки (Encoding Signature). BOM — это двухбайтный маркер в начале файл, который говорит о том, какая именно кодировка тут применена.

В интернете чаще всего используют UTF-8, она также прописана как предпочтительная в стандарте HTML5, так что уделю ей больше всего внимания.

Этот график построен в 2012-м, UTF-8 становилась доминирующей кодировкой. И всё ещё ею является.

Что такое UTF-8 и как она работает?

UTF-8 кодирует с помощью одного байта каждый кодовый пункт Unicode с 0 по 127 (как в ASCII). То есть если вы писали программу с использованием ASCII, а ваши пользователи применяют UTF-8, они не заметят ничего необычного. Всё будет работать как задумано. Обратите внимание, как это важно. Нам нужно было сохранить обратную совместимость с ASCII в ходе массового внедрения UTF-8. И эта кодировка ничего не ломает.

Как следует из названия, кодовый пункт состоит из 8 битов (один байт). В Unicode есть символы, которые занимают несколько байтов (вплоть до 6). Это называют переменной длиной. В разных языках удельное количество байтов разное. В английском — 1, европейские языки (с латинским алфавитом), иврит и арабский представлены с помощью двух байтов на кодовый пункт. Для китайского, японского, корейского и других азиатских языков используют по три байта.

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

И теперь мы, как по волшебству, пришли к соглашению, как закодировать шумерскую клинопись (Хабр её не отображает), а также значки emoji!

Подытожив сказанное: сначала читаем BOM, чтобы определить версию кодировки, затем преобразуем файл в кодовые пункты Unicode, а потом выводим на экран символы из набора Unicode.

Напоследок про UTF

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

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

Важно сделать это в самом начале , поскольку парсинг HTML может начаться заново, если в данный момент используется неправильная кодировка. Также узнать версию кодировки можно из заголовка Content-Type HTTP-запроса/ответа.

Если HTML-документ не содержит упоминания кодировки, спецификация HTML5 предлагает такое интересное решение, как BOM-сниффинг. С его помощью мы по маркеру порядка байтов (BOM) можем определить используемую кодировку.

Это всё?

Unicode ещё не завершён. Как и в случае с любым стандартом, мы что-то добавляем, убираем, предлагаем новое. Никакие спецификации нельзя назвать «завершёнными». Обычно в год бывает 1-2 релиза, найти их описание можно здесь.

Если вы дочитали до конца, то вы молодцы. Предлагаю сделать домашнюю работу. Посмотрите, как могут ломаться сайты при использовании неправильной кодировки. Я воспользовался этим расширением для Google Chrome, поменял кодировку и попытался открывать разные страницы. Информация была совершенно нечитаемой. Попробуйте сами, как выглядит бнопня. Это поможет понять, насколько важна кодировка.

Заключение

При написании этой статьи я узнал о Майкле Эверсоне. С 1993 года он предложил больше 200 изменений в Unicode, добавил в стандарт тысячи символов. По состоянию на 2003 год он считался самым продуктивным участником. Он один очень сильно повлиял на облик Unicode. Майкл — один из тех, кто сделал интернет таким, каким мы его сегодня знаем. Очень впечатляет.

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

Источник

Оцените статью