Linux Fair Queue Packet Scheduler (FQ)公平队列报文调度器
文章目录
- 简介
- 重要接口
- enqueue():
- dequeue() :
- 函数分析
- 红黑树的作用
- 什么时候会往红黑树上添加节点
- 什么时候把红黑树上的节点取下来
- 流下一次允许发包时间的计算
- 分数的作用
- 什么时候减分
- 什么时候加分
- 公平体现在哪里
- 总结
简介
FQ主要用于本地流量调节,某一个套接字上的所有报文被认为是一条流,这条流的速度可以设置在skb->sk上,如果没设置则使用公用的另一套调度。本文关注基于流的调度。
代码中会使用一个flows结构体对应一个套接字,并挂接在红黑树上。
突发避免(也叫pacing)功能:
传输层可以设置sk->sk_pacing_rate,作为这条流的发包速度,这个报文调度器会根据速度在每个包之间添加延时。
重要接口
enqueue():
在红黑树上找到这条流,如果不存在则创建一条流并添加进红黑树。添加skb到每条流的skb队列(fifo)。
dequeue() :
服务于循环中的流。
函数分析
static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch,struct sk_buff **to_free)
{struct fq_sched_data *q = qdisc_priv(sch);struct fq_flow *f;if (unlikely(sch->q.qlen >= sch->limit))return qdisc_drop(skb, sch, to_free);f = fq_classify(skb, q);if (unlikely(f->qlen >= q->flow_plimit && f != &q->internal)) {q->stat_flows_plimit++;return qdisc_drop(skb, sch, to_free);}f->qlen++;qdisc_qstats_backlog_inc(sch, skb);if (fq_flow_is_detached(f)) {struct sock *sk = skb->sk;fq_flow_add_tail(&q->new_flows, f);if (time_after(jiffies, f->age + q->flow_refill_delay))f->credit = max_t(u32, f->credit, q->quantum);if (sk && q->rate_enable) {if (unlikely(smp_load_acquire(&sk->sk_pacing_status) !=SK_PACING_FQ))smp_store_release(&sk->sk_pacing_status,SK_PACING_FQ);}q->inactive_flows--;}/* Note: this overwrites f->age */flow_queue_add(f, skb);if (unlikely(f == &q->internal)) {q->stat_internal_packets++;}sch->q.qlen++;return NET_XMIT_SUCCESS;
}
参数说明,skb就是要发送的数据包,sch就是对应的网卡设备上的队列,第三个参数不关注,这是有关锁而不能立即释放skb的需要。
简单来说,在条件允许的情况下,把skb加入对应的流。这里有个问题,为什么不直接把流挂在对应的套接字上,何必每次都去找。
static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb)
{struct rb_node **p, *parent;struct sk_buff *head, *aux;fq_skb_cb(skb)->time_to_send = skb->tstamp ?: ktime_get_ns();head = flow->head;if (!head ||fq_skb_cb(skb)->time_to_send >= fq_skb_cb(flow->tail)->time_to_send) {if (!head)flow->head = skb;elseflow->tail->next = skb;flow->tail = skb;skb->next = NULL;return;}p = &flow->t_root.rb_node;parent = NULL;while (*p) {parent = *p;aux = rb_to_skb(parent);if (fq_skb_cb(skb)->time_to_send >= fq_skb_cb(aux)->time_to_send)p = &parent->rb_right;elsep = &parent->rb_left;}rb_link_node(&skb->rbnode, parent, p);rb_insert_color(&skb->rbnode, &flow->t_root);
}
重点,设置skb的发送时间,这是由tcp自己的算好的,至于加入的是红黑树还是链表,为了便于理解,认为加入链表就好。
static struct sk_buff *fq_dequeue(struct Qdisc *sch)
{struct fq_sched_data *q = qdisc_priv(sch);struct fq_flow_head *head;struct sk_buff *skb;struct fq_flow *f;unsigned long rate;u32 plen;u64 now;if (!sch->q.qlen)return NULL;skb = fq_dequeue_head(sch, &q->internal);if (skb)goto out;now = ktime_get_ns();fq_check_throttled(q, now);
begin:head = &q->new_flows;if (!head->first) {head = &q->old_flows;if (!head->first) {if (q->time_next_delayed_flow != ~0ULL)qdisc_watchdog_schedule_ns(&q->watchdog,q->time_next_delayed_flow);return NULL;}}f = head->first;if (f->credit <= 0) {f->credit += q->quantum;head->first = f->next;fq_flow_add_tail(&q->old_flows, f);goto begin;}skb = fq_peek(f);if (skb) {u64 time_next_packet = max_t(u64, fq_skb_cb(skb)->time_to_send,f->time_next_packet);if (now < time_next_packet) {head->first = f->next;f->time_next_packet = time_next_packet;fq_flow_set_throttled(q, f);goto begin;}if (time_next_packet &&(s64)(now - time_next_packet - q->ce_threshold) > 0) {INET_ECN_set_ce(skb);q->stat_ce_mark++;}}skb = fq_dequeue_head(sch, f);if (!skb) {head->first = f->next;/* force a pass through old_flows to prevent starvation */if ((head == &q->new_flows) && q->old_flows.first) {fq_flow_add_tail(&q->old_flows, f);} else {fq_flow_set_detached(f);q->inactive_flows++;}goto begin;}prefetch(&skb->end);plen = qdisc_pkt_len(skb);f->credit -= plen;if (!q->rate_enable)goto out;rate = q->flow_max_rate;/* If EDT time was provided for this skb, we need to* update f->time_next_packet only if this qdisc enforces* a flow max rate.*/if (!skb->tstamp) {if (skb->sk)rate = min(skb->sk->sk_pacing_rate, rate);if (rate <= q->low_rate_threshold) {f->credit = 0;} else {plen = max(plen, q->quantum);if (f->credit > 0)goto out;}}if (rate != ~0UL) {u64 len = (u64)plen * NSEC_PER_SEC;if (likely(rate))len = div64_ul(len, rate);/* Since socket rate can change later,* clamp the delay to 1 second.* Really, providers of too big packets should be fixed !*/if (unlikely(len > NSEC_PER_SEC)) {len = NSEC_PER_SEC;q->stat_pkts_too_long++;}/* Account for schedule/timers drifts.* f->time_next_packet was set when prior packet was sent,* and current time (@now) can be too late by tens of us.*/if (f->time_next_packet)len -= min(len/2, now - f->time_next_packet);f->time_next_packet = now + len;}
out:qdisc_bstats_update(sch, skb);return skb;
}
参数说明,传入的参数是网卡队列,而不是某个流,返回的是一个skb。
大致流程没有问题,超时加该流有剩余分数即可以发包。
下面讨论几个重要的疑点:
- 红黑树的作用
- 分数的作用
- 公平体现在哪里
红黑树的作用
什么时候会往红黑树上添加节点
取下一个报文,但是该报文的发送时间还未超时,会将这个流取下,添加到红黑树上,排序根据是下一次允许发包时间。
什么时候把红黑树上的节点取下来
在函数fq_dequeue中的第一步,会顺序检查红黑树上的流,如果到了该流发包的时间,则把该流取下来,一直遍历到该某个流超时时间大于现在。
流下一次允许发包时间的计算
发完一个包之后,使用这个包的大小除以BtlBw,ns为单位,加上这个大小,就是下一次允许发包的时间。
/* Account for schedule/timers drifts.* f->time_next_packet was set when prior packet was sent,* and current time (@now) can be too late by tens of us.*/if (f->time_next_packet)len -= min(len/2, now - f->time_next_packet);
在计算下一个超时时间时还有这么一段代码,意思是如果优先队列先发送了,那么这个队列发包就会延后了,这里给这个流加速。
分数的作用
什么时候减分
当一个包发完之后,分数需要相应地减去包大小的分数。
什么时候加分
当分数被减到0及以下之后,每次都会加一定的分数,大小是两倍的mtu。
公平体现在哪里
公平就是体现在分数,分数是以字节计算的,而不是包的个数计算的,比如,流1,每次发大包1500字节,流2,每次发小包500字节,假设流1排在流2前面,分数初始值相等,假设都为1500,每次增加1500,两者的速度也是相等的。
- 第一轮,流1,共发送1500字节,分数减到0,流2,共发送0字节,分数1500,队列->流1->流2
- 第二轮,流1,共发送1500字节,分数加到1500,流2,共发送500字节,分数1000,队列->流2->流1
- 第三轮,。。。
- 第四轮,。。。
- 第五轮,流1,共发送3000字节,分数减到0,流2,共发送1500字节,分数1500,队列->流1->流2
以上流程会一直循环下去,所以长时间来看,保证了两条流发送的总的字节数是相等的。
总结
- 发包速率由套接字自己设定的速率控制,即BBR算法中或者其他算法,设置的BtlBw计算得到。
- 公平体现在分数上,使用分数保证了上层设置的速度。
Linux Fair Queue Packet Scheduler (FQ)公平队列报文调度器相关推荐
- 15-4 队列实现调度器
- Linux公平队列FQ配置
FQ (Fair Queue)是一个无类别(classless)的报文调度器,其主意用于本地产生的流量.设计为控制每个流的发送节奏(pacing).FQ完成流的区分,并且可完成TCP层要求的发送节奏. ...
- hadoop3 Yarn容量(Capacity Scheduler)调度器和公平(Fair Scheduler)调度器配置
文章目录 组件模块说明 容量调度器(Capacity Scheduler) 容量调度器特点 公平调度器(Fair Scheduler) 配置容量调度器案例 例子1 例子2 例子3 例子4 配置公平调度 ...
- 【Linux 内核】CFS 调度器 ⑥ ( CFS 调度器就绪队列 cfs_rq | Linux 内核调度实体 sched_entity | “ 红黑树 “ 数据结构 rb_root_cached )
文章目录 一.CFS 调度器就绪队列 cfs_rq 二.Linux 内核调度实体 sched_entity 三." 红黑树 " 数据结构 rb_root_cached 一.CFS ...
- 第一次作业:Linux 2.6.32的进程模型与调度器分析
1.前言 本文分析的是Linux 2.6.32版的进程模型以及调度器分析.在线查看 源码下载 本文主要讨论以下几个问题: 什么是进程?进程是如何产生的?进程都有那些? 在操作系统中,进程是如何被管理 ...
- Linux进程调度 - 实时调度器 LoyenWang
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 返璞归真的Linux BFS调度器
自Linux 2.6以来(严格说应该是2.5),O(n)调度器被人们认为是一种千年之前就应该抛弃的东西被重重的甩开了,此后出现了O(1),CFS等,再也没人提起O(n)了.说实话,Linux的调度器远 ...
- Yarn调度器和调度算法(FIFO、容量调度器 与 公平调度器)
目录 Yarn调度器和调度算法 一.先进先出调度器(FIFO) 二.容量调度器(Capacity Scheduler) 1. 容量调度器特点 2. 容量调度器资源分配算法 三.公平调度器(Fair S ...
- MapReduce多用户任务调度器——容量调度器(Capacity Scheduler)原理和源码研究
前言:为了研究需要,将Capacity Scheduler和Fair Scheduler的原理和代码进行学习,用两篇文章作为记录.如有理解错误之处,欢迎批评指正. 容量调度器(Capacity Sch ...
最新文章
- php获取网页标题接口,PHP获取网页标题的3种实现方法代码实例
- BERT在小米NLP业务中的实战探索
- golang goroutine 退出方法
- 一张图了解javaJwt
- 如何结合PICgo,Typora以及阿里云对象存储OSS搭建自己图床写博客
- 非常不错的文章,囊括啦高性能、高可用的分布式架构体系所有名词
- 屌炸天,Oracle 发布了一个全栈虚拟机 GraalVM,支持 Python
- 2021吉林高考26日几点可以查询成绩,2021吉林高考成绩查分时间及入口
- initramfs 工作原理
- 95-866-040-源码-吞吐量-提升吞吐的利器 MicroBatch
- 从零实现深度学习框架——Softmax回归中的数值稳定
- [PHP] PHP+MYSQL留言板制作
- 呼叫中心行业,引领时代进步
- 260道网络安全工程师面试题(附答案)
- 数学之美系列好文,强推
- 【论文阅读】水下机器人控制视觉伺服部分
- 怎样提取PDF文件其中一页
- 创建新的domian域
- 判断TTS语音朗读是否结束
- hust 1570 Lazy. Lazy. Laaaaaaaaaaaazy!