Komputery w sieci lokalnej żądają najczęściej - co jest jednym z powodów tej pracy - większych przepustowości niż udostępniane przez jej łącza wyjściowe. Oznacza to, że przez większość czasu komputer je obsługujący przechowuje pewną liczbę pakietów w buforze związanym z interfejsem wyjściowym i wybiera jeden z nich za każdym razem, gdy dostanie od karty sieciowej informację o zakończeniu transmisji poprzedniego.
Od wersji 2.2.x w jądrze systemu Linux bufory te (nazywane dalej
kolejkami) nadzoruje pakiet iproute2
, pozwalający na
zdefiniowanie algorytmu obsługi kolejki (a dokładniej dwóch,
wejściowej i wyjściowej) dla każdego interfejsu sieciowego. Algorytm
ten budowany jest z bloków, z których każdy obsługuje dwa podstawowe
polecenia: wstawienia pakietu do kolejki oraz wskazania pakietu do
wysłania. Bloki te różnią się złożonością i sposobem działania, od
prostych kolejek FIFO (np. domyślnie używany moduł pfifo_fast
),
poprzez kształtujące wielkość wykorzystywanego pasma (np. tbf
),
do złożonych, dzielących pakiety na klasy, z których każda może być
obsługiwana przez inny algorytm wyboru pakietu (jak to się dzieje
w przypadku priq
, czy najczęściej wspominanego
cbq
). W ostatnim przypadku możliwe jest stworzenie drzewa klas,
w którego korzeniu znajduje się blok odpowiedzialny za obsługę
wszystkich pakietów, który wybiera jeden z podwęzłów do realizacji
żądań wstawienia lub wyboru pakietu do wysłania. Dodatkową
elastyczność zapewnia możliwość zdefiniowania dla każdej z klas listy
filtrów, które na podstawie zawartości pakietu czy informacji o nim
(np. pól struktury sk_buff
, stosowanej przez jądro do opisu
pojedynczego pakietu) mogą zdecydować o jego odrzuceniu lub
przekierowaniu do innej klasy.
Przykładowa hierarchia wykorzystująca kilka różnych bloków mogłaby wyglądać jak na rysunku 3.3 (przykład z [15]).
Pary numerów oddzielonych dwukropkiem są identyfikatorami klas;
oznaczenie 1:0 wskazuje zawsze korzeń drzewa. Prio
jest
algorytmem, który rozdziela ruch przychodzący do kilku klas na
podstawie zawartości pola TOS pakietu (domyślnie ruch ,,ciężki'' trafi
do gałęzi 1:3, a ruch interaktywny do 1:1 i 1:2. Gałęzie te są
obsługiwane przez niezależne algorytmy: sfq
i tbf
. Kiedy
system zażąda od węzła 1:0 wyboru pakietu do wysłania, ten -- zgodnie
z regułami algorytmu prio
-- będzie przekazywał żądanie kolejno do
klas 1:1, 1:2 oraz 1:3 i wyśle pierwszy otrzymany od nich pakiet.
Do budowy algorytmów służy program tc
;
hierarchię z rys. 3.3 stworzono poleceniami:
Pierwsze z nich przydziela algorytm prio
do obsługi klasy 1:0, co
powoduje automatyczne utworzenie gałęzi 1:1, 1:2 i 1:3, a kolejne trzy
przypisują do nich algorytmy sfq
i tbf
.
Od strony technicznej każdy z algorytmów obsługi kolejki jest
opisywany przez strukturę Qdisc_ops
zawierającą wskaźniki do
udostępnianych przez niego funkcji. Ważniejsze z nich to:
enqueue
- dodaje pakiet do kolejki
dequeue
- wybiera jeden pakiet z kolejki do wysłania
requeue
- wstawia pakiet z powrotem do kolejki
(odwrotność dequeue);
init
- inicjuje nowy algorytm obsługi kolejki;
destroy
- usuwa algorytm i wykorzystywane przez niego
dane;
reset
- zeruje informacje o stanie algorytmu,
przywracając go do stanu pierwotnego;
change
- zmienia parametry algorytmu.
Dodatkowo w strukturze Qdisc_ops
znajduje się pole
cls_ops
, będące wskaźnikiem do struktury Qdisc_class_ops
zawierającej funkcje:
graft
, leaf
, get
, put
,
change
, delete
i walk
),
tcf_chain
,
bind_tcf
, unbind_tcf
),
dump
).
Ważnym szczegółem jest to, że żądania enqueue
i dequeue
,
nawet w przypadku wielopoziomowego drzewa klas, wysyłane są przez
system tylko do jego korzenia, więc do klasy z identyfikatorem 1:0. Jej
algorytm ma całkowitą swobodę jeśli chodzi o ich obsługę,
w szczególności może je odrzucić lub skierować do dowolnej
innej klasy. Najczęściej oba żądania przekazywane są w dół drzewa wg
reguł algorytmów napotykanych po drodze, co oznacza że podczas
enqueue
pakiet przekazywany jest w stronę liści, a podczas
dequeue
- w stronę korzenia (wraca wzdłuż ścieżki wywołań
funkcji dequeue
kolejnych klas jako jej wynik). Wynika z tego,
że jeśli algorytm przyjmie pakiet i dostanie potwierdzenie o jego
przyjęciu od którejś ze swoich podklas, to (o ile do hierarchii nie
zostaną dodane filtry mogące m.in. przekierowywać pakiety do dowolnych
klas) będzie też kontrolował jego wybór.