나는 linux-htb QDisc와 linux tc QDisc의 큐잉 메커니즘을 전반적으로 이해하려고 노력하고 있습니다.
내가 수집할 수 있는 것: TX 중에 패킷은 Linux tc 내의 대기열에 보관됩니다. 기본적으로 이 대기열은 txqueuelen이 1000인 pfifo_fast QDisc를 따릅니다. 패킷 스케줄러는 이 대기열에서 패킷을 빼내고 이를 TX 드라이버 대기열(링 버퍼)에 배치합니다.
linux-htb를 사용하면 기본 대기열만 txqueuelen을 상속합니다. [협회]. 내 질문: 트리를 고려하십시오(속도는 괄호() 안에 kbits/sec로 지정됨).
1: root qdisc (class htb)
(100)
/ | \
/ | \
/ | \
1:1 1:2 1:3 parent qdiscs (class htb)
(30) (10) (60)
각 상위 HTB 클래스(1:1, 1:2 및 1:3)에 대해 내부 대기열이 유지됩니까? 그렇다면 대기열 길이는 얼마나 됩니까? 그렇지 않은 경우 실제로 유지되는 대기열 수와 목적은 무엇입니까? 대기열 길이는 얼마나 됩니까?
큐잉 규율(QDisc)은 정확히 무엇을 의미합니까? 사용된 데이터 구조(큐)의 속성인가요? 아니면 패킷 스케줄러의 속성입니까? 아니면 둘 다의 조합인가요?
HTB QDisc의 소스코드를 읽는 중 [협회], 직접 대기열이라는 것을 발견했습니다. 직접 대기열이란 무엇입니까?
가능하다면 관련 출처에 대한 링크를 제공해주세요.
답변1
제가 직접 소스 코드를 읽고 연구한 적이 있으므로 제 질문에 답변해 드리겠습니다. 내가 직접 연구 작업을 수행하지 않았다면 Frostschutz와 sourcejedi의 답변이 큰 도움이 될 것입니다. 내 지식으로는 정확해 보입니다(자세하지는 않지만 출발점을 제공합니다). 나머지 연구는 직접 수행하세요. ).
일부 이론: 대기열 규율에는 클래스형과 클래스형이 아닌 두 가지 종류가 있습니다.
우아한(sourcejedi의 답변에 따르면) 규율은 유연합니다. 이를 통해 서브클래스 qdiscs를 첨부하고 가능하면 다른 클래스와 대역폭을 공유할 수 있습니다. 리프 클래스에는 클래스가 없는 qdisc(기본/기본 qdisc)가 연결되어 있습니다(기본 qdisc라고도 함). 이러한 기본 qdisc에 의해 관리되는 대기열은 패킷이 대기열에 추가되고 대기열에서 제외되는 곳입니다. 패킷은 해당 클래스에 해당하는 알고리즘을 통해 이러한 클래스에서 대기열에서 제외되고 대기열에 추가됩니다. qdisc와 유사한 것의 예로는 HTB 및 CBQ가 있습니다.
계급이 없는qdisc는 기본 또는 기본 qdisc이며, 하위 qdisc를 연결할 수 없고 대역폭을 공유할 수 없다는 점에서 엄격합니다. 간단히 말해서, 그들은 그 자체입니다. 이러한 qdisc에는 qdisc에 해당하는 알고리즘에 따라 패킷을 대기열에 넣거나 대기열에서 빼는 대기열이 있습니다. 클래스 없는 qdisc의 예: pfifo, bfifo, pfifo_fast(기본적으로 Linux tc에서 사용됨), tbf, sfq 등
질문의 예제 트리에서 각 리프 htb 클래스 1:1, 1:2 및 1:3에는 기본 qdisc가 첨부되어 있으며 기본적으로 pfifo(pfifo_fast 아님)입니다. 리프에 부착된 기본 qdisc는 tc
다음과 같이 사용자 공간 유틸리티를 사용하여 변경할 수 있습니다:
tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 5
tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10
이에 대한 자세한 내용은 다음에서 확인할 수 있습니다.HTB Linux 대기열 규칙 매뉴얼. 따라서 트리는 실제로 다음과 같습니다.
1: root qdisc (class htb)
(100)
/ | \
/ | \
/ | \
1:1 1:2 1:3 parent qdiscs (class htb)
(30) (10) (60)
|| || || -----> pfifo qdiscs (queue length: txqueuelen (default, can be changed by tc utitlity))
txqueuelen 매개변수는 인터페이스별 매개변수입니다. 즉, 매개변수는 인터페이스의 속성이며 사용되거나 iproute2
변경 될 수 있습니다 ifconfig
. 기본적으로 해당 값은 1000입니다. 다음을 통해 200으로 변경하는 방법에 대한 예는 다음과 같습니다 iproute2
.
ip link set eth0 txqueuelen 200
리프 노드가 생성되면(HTB qdisc의 컨텍스트에서) pfifo qdisc는 기본적으로 리프 클래스에 연결됩니다. 이 pfifo는 인터페이스의 txqueuelen 대기열 제한으로 초기화됩니다. 이는 함수에서 htb_change_class()
찾을 수 있습니다.sch_htb.c, 1395행:
/* create leaf qdisc early because it uses kmalloc(GFP_KERNEL)
* so that can't be used inside of sch_tree_lock
* -- thanks to Karlis Peisenieks
*/
new_q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
classid, NULL);
pfifo 대기열의 기본 대기열 길이는 다음을 참조하세요.sch_fifo.c, 61행:
u32 limit = qdisc_dev(sch)->tx_queue_len;
커널이 패킷을 대기열에 넣거나 대기열에서 빼려고 할 때 루트 qdisc(클래스가 있거나 클래스가 없을 수 있음)와 직접 상호 작용합니다. 루트 qdisc가 분류되고 자식이 있으면 먼저 패킷을 분류합니다(패킷을 보낼 자식을 결정합니다). 이를 달성하기 위한 커널 소스 코드:sch_htb.c, 209행:
static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
int *qerr)
주석을 읽은 후 함수가 다음 중 하나를 반환한다는 것을 쉽게 추론할 수 있습니다.
패킷을 삭제해야 하는 경우 NULL을 반환합니다.
-1: 패킷이 direct_queue에 대기해야 하는 경우
리프 노드(패킷이 실제로 끝나는 기본 qdisc 포함) 이 함수는 패킷이 대기열에 있어야 하는 리프 노드를 반환할 때까지 트리의 모든 내부 노드(클래스)를 탐색합니다.
대기열에서 제거할 때 각 클래스는 qdisc와 관련된 알고리즘을 따라 대기열에서 제거할 자식을 결정하고, 자식은 리프 클래스에 연결된 기본 qdisc에서 패킷이 대기열에서 제거될 때까지 동일한 작업을 수행합니다. 이는 또한 하위 클래스의 비율이 상위 클래스의 비율을 초과하지 않도록 보장합니다. (패킷 통과 여부는 부모가 결정하기 때문입니다.) HTB에서 Dequeueing 소스에 대해 배운 적이 없어서 소스를 제공할 수 없습니다.
직접 대기열에 넣기: HTB qdisc에 의해 유지되는 특수 내부 FIFO 대기열로, 패킷이 하드웨어 속도로 대기열에서 제외됩니다. 대기열 길이는 txqueuelen입니다. HTB가 패킷을 sub-qdisc 중 하나로 분류할 수 없고 기본값이 지정되지 않은 경우 패킷은 직접 대기열에 들어가게 됩니다.
그래서 내 질문에 대한 대답은 다음과 같습니다.
예, 리프 노드이므로 기본적으로 pfifo 대기열이며 인터페이스의 대기열 길이 txqueuelen은 기본적으로 1000이며 변경할 수 있습니다.
큐잉 분야는 하나의 패키지에 결합된 알고리즘과 큐와 같습니다! 저에게 묻는다면 큐잉 규칙은 큐 유형과 패킷 스케줄러의 속성입니다(여기서 패킷 스케줄러는 패킷을 큐에 넣거나 빼는 알고리즘을 나타냅니다). 예를 들어 대기열은 pfifo 또는 bfifo 유형일 수 있습니다. 큐에 넣기와 빼기에 사용되는 알고리즘은 동일하지만 큐 길이는 바이트 fifo(bfifo) 단위로 측정됩니다. 바이트 제한에 도달하면 패킷이 bfifo에서 삭제됩니다. 기본 바이트 제한은 다음과 같이 계산됩니다
mtu*txqueuelen
. 예를 들어, 패킷이 대기열에 추가되면 패킷 길이가 현재 대기열 길이에 추가됩니다. 마찬가지로, 패킷이 대기열에서 제거되면 대기열 길이에서 패킷 길이가 뺍니다.위에서 이미 답변했습니다.
사람들이 확인할 수 있는 일부 출처는 다음과 같습니다.
답변2
부인 성명:질문이 너무 많아요. 제가 10년 동안 HTB를 사용하지 않았나요? 그래서 자신 있게 대답할 수가 없어요. 하지만 지금까지 답변이 전혀 없으므로 이 내용이 여전히 도움이 될 수 있습니다.
각 상위 HTB 클래스(1:1, 1:2 및 1:3)에 대해 내부 대기열이 유지됩니까?
그것들은 각각 qdisc 큐 구조로 표현되는 리프 클래스이므로 내부 큐로 간주된다고 가정합니다. 대기열 길이가 확실하지 않습니다. 죄송합니다.
struct htb_class_leaf {
int deficit[TC_HTB_MAXDEPTH];
struct Qdisc *q;
} leaf;
Qdisc 구조는 include/net/sch_generic.h에 정의되어 있습니다.
큐잉 규율(QDisc)은 정확히 무엇을 의미합니까?
상황에 따라 다르지만 기본적으로 패킷이 대기열에 추가되고 대기열에서 제외되는 커널 API입니다. 따라서 QDisc는 들어오는 패킷이 다시 전송되는(또는 완전히 삭제되는) 순서(또는 타이밍)를 어느 정도 제어할 수 있습니다. 이는 HTB, SFQ 또는 PRIO와 같은 QDisc가 우선순위 지정 또는 대역폭 제한 부과와 같은 다양한 방식으로 트래픽을 형성하는 방식입니다.
Generally, queueing discipline ("qdisc") is a black box,
which is able to enqueue packets and to dequeue them (when
device is ready to send something) in order and at times
determined by algorithm hidden in it.
HTB는 그러한 여러 알고리즘 중 하나일 뿐입니다.
직접 대기열이란 무엇입니까?
API의 일부는 아니지만 내부적으로 처리되므로 HTB 알고리즘의 일부로 생각할 수 있습니다.
의도적으로 패킷을 분류 X:0
하거나 기본 클래스가 존재하지 않는 경우 HTB는 해당 패킷을 별도의 대기열에 넣기로 결정하고 대기열에서 제거할 때 해당 패킷을 먼저 보내려고 시도합니다.
* [...] If we end up with classid MAJOR:0 we enqueue the skb into special
* internal fifo (direct). These packets then go directly thru. If we still
* have no valid leaf we try to use MAJOR:default leaf. It still unsuccessful
* then finish and return direct queue.
...그리고이것은 직접 패킷을 처음으로 대기열에서 제거하는 곳입니다..
이는 일반적으로 잘못된 구성(존재하지 않는 클래스에 패킷 전송)의 결과이지만 HTB 개발자는 이 경우 모든 트래픽을 삭제하는 대신 모든 트래픽을 통과해야 한다고 결정했습니다(너무 방해가 됨).
답변3
일부 빠른 퍼지 메모리 기반 검색:
HTB는 유연합니다. 기본적으로 각 리프 클래스에는 FIFO가 있고 아마도 FIFO의 기본 구성을 사용할 수도 있습니다. 그러나 리프 클래스에는 PFIFO 또는 FQ_CODEL을 사용할 수도 있습니다. 여기에서 "선택적으로 리프 클래스에 대기열 규칙 연결"을 참조하세요.http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm
내 생각엔 당신이 달리면 그들을 볼 수 있을 것 같아요 tc qdisc show
.
"이 '직접' 큐는 필터 중 하나가 htb qdisc의 '0' 클래스 ID를 명시적으로 대상으로 하는 경우에만 사용됩니다."https://lists.bufferbloat.net/pipermail/cerowrt-devel/2013-June/006507.html. 직접 대기열이 아직 형성되지 않았습니다. 분명히 이제 직접 대기열의 길이를 제어하는 것이 가능해졌지만 tc가 이를 어떻게 지원하는지(또는 지원하는지)는 모르겠습니다.https://patchwork.ozlabs.org/patch/225546/
별도의 "패킷 스케줄러"가 있다고 말하지는 않겠습니다. 패킷 스케줄러는 QDisc입니다(그러나man tc-tbf
"트래픽을 예약하지 않음"이라고 주장합니다. 이는 순서를 다시 지정하지 않음을 의미합니다.