`

Linux内核中流量控制(3)

阅读更多
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
5.2 FIFO

FIFO算法在net/sched/sch_fifo.c中定义, 既可以单独使用, 也可以被其他流控算法(如TBF)作为内
部流控算法使用.

5.2.1 流控结构
FIFO分两种, 一种是PFIFO, 一种是BFIFO, 分别按包数和字节数进行FIFO的流量控制.
 
// 私有数据结构, 就是流量参数
struct fifo_sched_data
{
 u32 limit;
};
// 包数流控
struct Qdisc_ops pfifo_qdisc_ops = {
 .id  = "pfifo",
 .priv_size = sizeof(struct fifo_sched_data),
 .enqueue = pfifo_enqueue,
 .dequeue = qdisc_dequeue_head,
 .requeue = qdisc_requeue,
 .drop  = qdisc_queue_drop,
 .init  = fifo_init,
 .reset  = qdisc_reset_queue,
 .change  = fifo_init,
 .dump  = fifo_dump,
 .owner  = THIS_MODULE,
};

// 字节数流控
struct Qdisc_ops bfifo_qdisc_ops = {
 .id  = "bfifo",
 .priv_size = sizeof(struct fifo_sched_data),
 .enqueue = bfifo_enqueue,
 .dequeue = qdisc_dequeue_head,
 .requeue = qdisc_requeue,
 .drop  = qdisc_queue_drop,
 .init  = fifo_init,
 .reset  = qdisc_reset_queue,
 .change  = fifo_init,
 .dump  = fifo_dump,
 .owner  = THIS_MODULE,
};
在这两个结构中, 处理enqueue, init, reset, dump外, 其他操作函数都是用流控缺省操作函数.

5.2.2 初始化

static int fifo_init(struct Qdisc *sch, struct rtattr *opt)
{
// FIFO私有数据
 struct fifo_sched_data *q = qdisc_priv(sch);
 if (opt == NULL) {
// 如果没提供流量参数情况
// limit缺省取网卡的发送队列长度, 而且至少为1
  u32 limit = sch->dev->tx_queue_len ? : 1;
// 如果是字节流控, 用的是队列长度*MTU作为限制值
  if (sch->ops == &bfifo_qdisc_ops)
   limit *= sch->dev->mtu;
// 如果是包流控, 直接用队列长度作为限制值
  q->limit = limit;
 } else {
// 提供合法流量参数时用提供的流控值作为流控限制参数
  struct tc_fifo_qopt *ctl = RTA_DATA(opt);
  if (RTA_PAYLOAD(opt) < sizeof(*ctl))
   return -EINVAL;
  q->limit = ctl->limit;
 }
 return 0;
}

5.2.3 入队

// 字节流控入队
static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
 struct fifo_sched_data *q = qdisc_priv(sch);
// 如果当前队列数据总长加数据包长度不超过限制值, 将数据包添加到队列尾
 if (likely(sch->qstats.backlog + skb->len <= q->limit))
  return qdisc_enqueue_tail(skb, sch);
 return qdisc_reshape_fail(skb, sch);
}

// 包数流控入队
static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
 struct fifo_sched_data *q = qdisc_priv(sch);
// 如果缓存队列数据包数小于限制值, 将数据包添加到队列尾
 if (likely(skb_queue_len(&sch->q) < q->limit))
  return qdisc_enqueue_tail(skb, sch);
 return qdisc_reshape_fail(skb, sch);
}
 
5.2.4 输出

// 就是将私有数据limit作为参数返回, 用的应该是netlink套接口
static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
{
 struct fifo_sched_data *q = qdisc_priv(sch);
 struct tc_fifo_qopt opt = { .limit = q->limit };
 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 return skb->len;
rtattr_failure:
 return -1;
}
 
5.3 TBF(Token Bucket Filter queue, 令牌桶过滤队列)

TBF算法是一种限制流量算法,基本原理是将入队的数据包缓存在队列中,同时按指定的速度产生令
牌,只有拥有令牌才能数据包出队,这样就控制了出口流量,目前netfilter中的limit匹配实际也是
用TBF算法, 该算法在net/sched/sch_tbf.c中定义,在该文件的注释中定义的算法如下:
/*
 Description.
 ------------
 A data flow obeys TBF with rate R and depth B, if for any
 time interval t_i...t_f the number of transmitted bits
 does not exceed B + R*(t_f-t_i).
 Packetized version of this definition:
 The sequence of packets of sizes s_i served at moments t_i
 obeys TBF, if for any i<=k:
 s_i+....+s_k <= B + R*(t_k - t_i)
 Algorithm.
 ----------
 Let N(t_i) be B/R initially and N(t) grow continuously with time as:
 N(t+delta) = min{B/R, N(t) + delta}
 If the first packet in queue has length S, it may be
 transmitted only at the time t_* when S/R <= N(t_*),
 and in this case N(t) jumps:
 N(t_* + 0) = N(t_* - 0) - S/R.
 
 Actually, QoS requires two TBF to be applied to a data stream.
 One of them controls steady state burst size, another
 one with rate P (peak rate) and depth M (equal to link MTU)
 limits bursts at a smaller time scale.
 It is easy to see that P>R, and B>M. If P is infinity, this double
 TBF is equivalent to a single one.
 When TBF works in reshaping mode, latency is estimated as:
 lat = max ((L-B)/R, (L-M)/P)
*/
 
5.3.1 操作结构定义

// TBF属性结构
struct tc_tbf_qopt
{
// 速率
 struct tc_ratespec rate;
// 峰值速率
 struct tc_ratespec peakrate;
// 限制值
 __u32  limit;
// 缓冲区大小
 __u32  buffer;
// MTU参数
 __u32  mtu;
};
// TBF算法私有数据结构, 这个结构让人想起limit匹配结构, 也是分两部分, 固定参数和可变参数
struct tbf_sched_data
{
/* Parameters */
// 以下是固定参数
// 流量限制值
 u32  limit;  /* Maximal length of backlog: bytes */
// 桶深, 也就是缓冲区大小
 u32  buffer;  /* Token bucket depth/rate: MUST BE >= MTU/B */
// 网卡MTU
 u32  mtu;
// 允许的最大包长
 u32  max_size;
 struct qdisc_rate_table *R_tab;
 struct qdisc_rate_table *P_tab;
/* Variables */
// 以下是变化数据, 在每次算法计算时会发生改变
// B型token的数量
 long tokens;   /* Current number of B tokens */
// P型token的数量
 long ptokens;  /* Current number of P tokens */
// 时间
 psched_time_t t_c;  /* Time check-point */
// 定时器
 struct timer_list wd_timer; /* Watchdog timer */
// 内部流控结构, 缺省使用bfifo
 struct Qdisc *qdisc;  /* Inner qdisc, default - bfifo queue */
};

// TBF流控算法操作结构
static struct Qdisc_ops tbf_qdisc_ops = {
 .next  = NULL,
 .cl_ops  = &tbf_class_ops,
 .id  = "tbf",
 .priv_size = sizeof(struct tbf_sched_data),
 .enqueue = tbf_enqueue,
 .dequeue = tbf_dequeue,
 .requeue = tbf_requeue,
 .drop  = tbf_drop,
 .init  = tbf_init,
 .reset  = tbf_reset,
 .destroy = tbf_destroy,
 .change  = tbf_change,
 .dump  = tbf_dump,
 .owner  = THIS_MODULE,
};
// TBF类别操作结构
static struct Qdisc_class_ops tbf_class_ops =
{
 .graft  = tbf_graft,
 .leaf  = tbf_leaf,
 .get  = tbf_get,
 .put  = tbf_put,
 .change  = tbf_change_class,
 .delete  = tbf_delete,
 .walk  = tbf_walk,
 .tcf_chain = tbf_find_tcf,
 .dump  = tbf_dump_class,
};

5.3.2 初始化

static int tbf_init(struct Qdisc* sch, struct rtattr *opt)
{
// TBF私有数据
 struct tbf_sched_data *q = qdisc_priv(sch);
 if (opt == NULL)
  return -EINVAL;
// 获取当前实际
 PSCHED_GET_TIME(q->t_c);
// 初始化定时器
 init_timer(&q->wd_timer);
// 定时函数
 q->wd_timer.function = tbf_watchdog;
// 数据就是流控结构本身
 q->wd_timer.data = (unsigned long)sch;
// 内部流控初始化为noop_qdisc
 q->qdisc = &noop_qdisc;
// 调用tbf_change设置TBF流控结构参数
 return tbf_change(sch, opt);
}

// 定时器函数, 功能就是清除阻塞标识, 让网卡重新调度
static void tbf_watchdog(unsigned long arg)
{
 struct Qdisc *sch = (struct Qdisc*)arg;
// 清除阻塞标志
 sch->flags &= ~TCQ_F_THROTTLED;
// 重新进行网卡调度
 netif_schedule(sch->dev);
}

5.3.3 入队

// TBF是根据数据包长度而不是个数来进行流控的, 缺省内部流控结构是bfifo
static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
// TBF私有数据
 struct tbf_sched_data *q = qdisc_priv(sch);
 int ret;
// 如果数据包长度超过TBF允许最大长度, 丢包
 if (skb->len > q->max_size) {
  sch->qstats.drops++;
#ifdef CONFIG_NET_CLS_POLICE
  if (sch->reshape_fail == NULL || sch->reshape_fail(skb, sch))
#endif
   kfree_skb(skb);
  return NET_XMIT_DROP;
 }
// 调用内部流控结构的入队操作, 失败丢包
 if ((ret = q->qdisc->enqueue(skb, q->qdisc)) != 0) {
  sch->qstats.drops++;
  return ret;
 }
// 入队成功增加相关统计量
 sch->q.qlen++;
 sch->bstats.bytes += skb->len;
 sch->bstats.packets++;
 return 0;
}

// 重入队操作
static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
// TBF私有数据
 struct tbf_sched_data *q = qdisc_priv(sch);
 int ret;
// 调用内部流控结构的重入队操作, 成功则增加统计计数
 if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
  sch->q.qlen++;
  sch->qstats.requeues++;
 }
 return ret;
}

5.3.4 出队

// 长度转换为相应大小的令牌数
#define L2T(q,L)   ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log])
// 长度转换为相应大小的P令牌数
#define L2T_P(q,L) ((q)->P_tab->data[(L)>>(q)->P_tab->rate.cell_log])

static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
{
 struct tbf_sched_data *q = qdisc_priv(sch);
 struct sk_buff *skb;
// 调用内部流控结构的出队函数, 对于BFIFO来说就是直接取队列头的那个数据包
 skb = q->qdisc->dequeue(q->qdisc);
 if (skb) {
// 取到数据包的情况
  psched_time_t now;
  long toks, delay;
  long ptoks = 0;
// 长度初始化为数据包长度
  unsigned int len = skb->len;
// 获取当前时间
  PSCHED_GET_TIME(now);
// 根据当前时间和上次流控计算时间的时间差来计算可用的令牌量
  toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer);
// 如果也按包数流控
  if (q->P_tab) {
// 计算当前可用的P令牌数: 当前P令牌数加新增令牌量
   ptoks = toks + q->ptokens;
// 如果超过了MTU值, 限制为MTU
   if (ptoks > (long)q->mtu)
    ptoks = q->mtu;
// 当前可用P令牌数减去当前数据包长度对应的P令牌量
   ptoks -= L2T_P(q, len);
  }
// 再加上原来已经有的令牌量
  toks += q->tokens;
// 如果令牌量超过了全部缓冲区大小, 限制为缓冲区大小
  if (toks > (long)q->buffer)
   toks = q->buffer;
// 当前令牌量减去当前数据包长度对应的令牌量
  toks -= L2T(q, len);
// 如果当前令牌量大于0, 表示桶中的令牌量允许发送该数据包
  if ((toks|ptoks) >= 0) {
// 更新流控时间
   q->t_c = now;
// 更新当前令牌数
   q->tokens = toks;
// 更新P令牌数
   q->ptokens = ptoks;
// 队列长度减
   sch->q.qlen--;
// 此时没阻塞
   sch->flags &= ~TCQ_F_THROTTLED;
// 返回数据包
   return skb;
  }
// 现在是令牌数不够, 说明要发送的数据量超过了限制值, 发生了阻塞
// 计算延迟时间值
  delay = PSCHED_US2JIFFIE(max_t(long, -toks, -ptoks));
// 至少定时一个时间片
  if (delay == 0)
   delay = 1;
// 修改定时器的定时时间
  mod_timer(&q->wd_timer, jiffies+delay);
  /* Maybe we have a shorter packet in the queue,
     which can be sent now. It sounds cool,
     but, however, this is wrong in principle.
     We MUST NOT reorder packets under these circumstances.
     Really, if we split the flow into independent
     subflows, it would be a very good solution.
     This is the main idea of all FQ algorithms
     (cf. CSZ, HPFQ, HFSC)
   */
// 将数据包重新入队等待发送
  if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
   /* When requeue fails skb is dropped */
   sch->q.qlen--;
   sch->qstats.drops++;
  }
// 设置阻塞标识
  sch->flags |= TCQ_F_THROTTLED;
  sch->qstats.overlimits++;
 }
 return NULL;
}

5.3.5 丢包

static unsigned int tbf_drop(struct Qdisc* sch)
{
 struct tbf_sched_data *q = qdisc_priv(sch);
 unsigned int len = 0;
// 就是调用内部流控结构的类别操作结构中的丢包操作函数
 if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
  sch->q.qlen--;
  sch->qstats.drops++;
 }
 return len;
}

5.3.6 复位

static void tbf_reset(struct Qdisc* sch)
{
 struct tbf_sched_data *q = qdisc_priv(sch);
// 调用内部流控结构的复位函数
 qdisc_reset(q->qdisc);
// 复位TBF流控结构参数
// 清空队列长度
 sch->q.qlen = 0;
// 获取当前时间
 PSCHED_GET_TIME(q->t_c);
// B令牌数为缓冲区大小
 q->tokens = q->buffer;
// P令牌数为MTU值
 q->ptokens = q->mtu;
// 清除被阻塞标志
 sch->flags &= ~TCQ_F_THROTTLED;
// 删除定时器
 del_timer(&q->wd_timer);
}

5.3.7 生成内部流控结构

static struct Qdisc *tbf_create_dflt_qdisc(struct net_device *dev, u32 limit)
{
// 分配一个bfifo的流控结构
 struct Qdisc *q = qdisc_create_dflt(dev, &bfifo_qdisc_ops);
        struct rtattr *rta;
 int ret;
 if (q) {
// 分配动态流控参数空间,不知道为什么不直接用静态结构而用动态分配的, 该结构也不大
  rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
  if (rta) {
// 设置该流控的属性, 也就是BFIFO中的流控限制值的大小
// bfifo的change函数实际就是init函数
   rta->rta_type = RTM_NEWQDISC;
   rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt));
   ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
   ret = q->ops->change(q, rta);
   kfree(rta);
   if (ret == 0)
    return q;
  }
  qdisc_destroy(q);
 }
 return NULL;
}

5.3.8 更新控制参数

// 根据opt参数设置TBF流控结构私有参数
static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
{
 int err = -EINVAL;
// TBF私有数据
 struct tbf_sched_data *q = qdisc_priv(sch);
// 属性表, TCA_TBF_PTAB=3
 struct rtattr *tb[TCA_TBF_PTAB];
// TBF属性结构参数
 struct tc_tbf_qopt *qopt;
 struct qdisc_rate_table *rtab = NULL;
 struct qdisc_rate_table *ptab = NULL;
 struct Qdisc *child = NULL;
 int max_size,n;
// 解析rtattr结构属性值
 if (rtattr_parse_nested(tb, TCA_TBF_PTAB, opt) ||
     tb[TCA_TBF_PARMS-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_TBF_PARMS-1]) < sizeof(*qopt))
  goto done;
// 数据转换为具体的属性参数结构指针
 qopt = RTA_DATA(tb[TCA_TBF_PARMS-1]);
// 根据新流量速率表参数生成新节点返回(系统流量速率链表为空)或
// 拷贝到当前流量速率表链表的头节点返回流量速率表节点
 rtab = qdisc_get_rtab(&qopt->rate, tb[TCA_TBF_RTAB-1]);
 if (rtab == NULL)
  goto done;
// 如果设置了峰值速率
 if (qopt->peakrate.rate) {
// 如果峰值速率大于规定速率, 新生成峰值速率节点
  if (qopt->peakrate.rate > qopt->rate.rate)
   ptab = qdisc_get_rtab(&qopt->peakrate, tb[TCA_TBF_PTAB-1]);
  if (ptab == NULL)
   goto done;
 }
// 找一个大于限制值的数组项
 for (n = 0; n < 256; n++)
  if (rtab->data[n] > qopt->buffer) break;
// 计算最大数据包长度
 max_size = (n << qopt->rate.cell_log)-1;
 if (ptab) {
// 如果峰值速率控制非空
  int size;
// 计算峰值速率发送下的最大数据包长度
  for (n = 0; n < 256; n++)
   if (ptab->data[n] > qopt->mtu) break;
  size = (n << qopt->peakrate.cell_log)-1;
  if (size < max_size) max_size = size;
 }
// 计算出来的最大值小于0, 返回错误
 if (max_size < 0)
  goto done;
 if (qopt->limit > 0) {
// 根据流量限制值生成内部BFIFO流控结构
  if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL)
   goto done;
 }
 sch_tree_lock(sch);
 if (child)
  qdisc_destroy(xchg(&q->qdisc, child));
// 填充TBF流控私有结构参数
 q->limit = qopt->limit;
 q->mtu = qopt->mtu;
 q->max_size = max_size;
 q->buffer = qopt->buffer;
 q->tokens = q->buffer;
 q->ptokens = q->mtu;
 rtab = xchg(&q->R_tab, rtab);
 ptab = xchg(&q->P_tab, ptab);
 sch_tree_unlock(sch);
 err = 0;
done:
 if (rtab)
  qdisc_put_rtab(rtab);
 if (ptab)
  qdisc_put_rtab(ptab);
 return err;
}
 
5.3.9 释放

static void tbf_destroy(struct Qdisc *sch)
{
 struct tbf_sched_data *q = qdisc_priv(sch);
// 删除定时器
 del_timer(&q->wd_timer);
// 释放P,R流控速率控制表结构
 if (q->P_tab)
  qdisc_put_rtab(q->P_tab);
 if (q->R_tab)
  qdisc_put_rtab(q->R_tab);
// 释放流控结构
 qdisc_destroy(q->qdisc);
}

5.3.10 输出

static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
{
 struct tbf_sched_data *q = qdisc_priv(sch);
// 数据包的数据尾用来存储流控参数
 unsigned char  *b = skb->tail;
 struct rtattr *rta;
// TBF选项结构
 struct tc_tbf_qopt opt;
// 数据包的数据尾定义为rtattr结构
 rta = (struct rtattr*)b;
// 扩展数据包数据长度
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
// 填充TBF选项结构: 限制值和当前P, R速率值, MTU, 缓冲区大小
 opt.limit = q->limit;
 opt.rate = q->R_tab->rate;
 if (q->P_tab)
  opt.peakrate = q->P_tab->rate;
 else
  memset(&opt.peakrate, 0, sizeof(opt.peakrate));
 opt.mtu = q->mtu;
 opt.buffer = q->buffer;
// 拷贝到skb中
 RTA_PUT(skb, TCA_TBF_PARMS, sizeof(opt), &opt);
// 属性数据长度
 rta->rta_len = skb->tail - b;
// 返回数据总长
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

// 输出类别结构
static int tbf_dump_class(struct Qdisc *sch, unsigned long cl,
     struct sk_buff *skb, struct tcmsg *tcm)
{
 struct tbf_sched_data *q = qdisc_priv(sch);
// 只有一个类别, 返回失败
 if (cl != 1)  /* only one class */
  return -ENOENT;
// 填充TCMSG结构
 tcm->tcm_handle |= TC_H_MIN(1);
 tcm->tcm_info = q->qdisc->handle;
 return 0;
}
 
...... 待续 ......

发表于: 2007-07-29,修改于: 2007-07-29 15:29,已浏览3828次,有评论10条 推荐 投诉
	网友: 本站网友 	时间:2007-08-17 14:56:55 IP地址:222.68.182.★
	

tbf_dequeue这个函数没看太懂,想请教一下:

1. R_tab和P_tab是什么关系?分别代表什么意思?

2. 这一段程序是什么意思?能否更详细解释一下?谢谢

        if (q->P_tab) {

            ptoks = toks + q->ptokens;

            if (ptoks > (long)q->mtu)

                ptoks = q->mtu;

            ptoks -= L2T_P(q, len);

        }

3. 这个数据结构struct tbf_sched_data中的mtu究竟是什么意思?起什么作用呢?

谢谢了


	网友: yfydz 	时间:2007-08-18 12:06:10 IP地址:123.116.100.★
	

1. R_tab和P_tab是用于流控计算的,一个根据长度计算令牌数,一个根据包数计算流控,

2. 计算P令牌

3.MTU是在包数流控时作为最大包数限制


	网友: 本站网友 	时间:2007-08-20 10:18:34 IP地址:222.68.182.★
	

可是MTU不是指最大传输单元吗?应该是指一个数据包在当前链路上的最大字节数。它怎么能用来表示包流控时的最大包数限制?就是这里不太明白


	网友: 本站网友 	时间:2007-08-21 08:45:20 IP地址:60.191.4.★
	

//普通令牌桶表R_tab;

//峰值令牌桶表P_tab:

mtu指最打传输峰值  

if (q->P_tab) {

            //有峰值速率限制

            ptoks = toks + q->ptokens;//总峰值令牌数

            if (ptoks > (long)q->mtu)

                ptoks = q->mtu;//不能超过峰值令牌桶容量

            ptoks -= L2T_P(q, skb->len);//本包消耗的峰值令牌数

        }

        toks += q->tokens;//累计普通令牌数

        if (toks > (long)q->buffer)

            toks = q->buffer;//不能超过普通令牌桶容量

        toks -= L2T(q, skb->len);//本包消耗的普通令牌数


	网友: 本站网友 	时间:2007-08-21 11:21:39 IP地址:222.68.182.★
	

谢谢。不过峰值令牌为什么要用MTU来度量呢?两个从道理上似乎没什么关系。


	网友: 本站网友 	时间:2007-10-12 15:18:26 IP地址:222.64.17.★
	

强烈的顶顶顶!!! 

 15G空间=5个网站=500元/年 可免费试用 

www.abcnic.com    QQ:1012727

5GB 独立WEB空间、5GB 企业邮箱空间、5GB MSSQL数据库   

IIS连接数据 500 个、500GB/月流量、共享日志文件空间 

数据库功能 

支持5GB MSSQL数据库空间,5个用户数据库、Access 

主机功能支持 

采用安全稳定的Win2003 .net2.0 架构 

支持ASP、PHP、ASP.NET、PERL等脚本、支持自定义CGI 

全面支持.net2.0版本,独立的Application应用池,

支持SSI(Shtml),支持FrontPage扩展 

可免费自行绑定5个域名、500个解析、500个子域名

 企业邮箱功能 

赠送5GB 超大企业邮箱,500个Email企业邮箱用户 

自动回复、自动转发、POP3、SMTP收发信、SMTP发信认证 

邮件过滤、邮件拒收、邮件夹管理、邮件域管理、定制邮件数 


	网友: dreamagean 	时间:2009-03-24 20:38:37 IP地址:219.238.94.★
	

请您讲一下L2T函数的实现?

看了好长时间不能理解。

谢谢!


	网友: dreamagean 	时间:2009-03-25 09:13:49 IP地址:114.255.20.★
	

请大牛解释一下!


	网友: 本站网友 	时间:2009-09-01 10:26:06 IP地址:219.143.137.★
	

l2t用来算btoken数

qdisc_rate_table里面的data数组记录令牌数

(q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log]这一句就是去找由L和q决定的令牌数

计算当前令牌量是通过时间和缓冲长度来完成

toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer);


	网友: allen 	时间:2010-05-24 14:34:36 IP地址:210.3.50.★
	

#define PSCHED_GET_TIME(stamp) ((stamp) = (get_jiffies_64()<<PSCHED_JSCALE))



请问下这里的PSCHED_JSCALE的含义是什么?以及通过proc提供给user space用的psched_tick_per_us, psched_us_per_tick的含义?
分享到:
评论

相关推荐

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    基于Linux LQL流量控制系统的研究与实现

    该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...

    Linux高级路由和流量控制

    15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...

    编写Linuxc操作系统设备驱动程序概述

    在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...

    xt_fset:扩展到Linux内核netfilter子系统(iptables)(插件),使您可以通过发送ICMP包远程操作linux内核ipset(向ipset中添加或删除一些ip地址)。

    xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...

    Linux Kernel v4.19.1 Stable.zip

    Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图

    Linux的高级路由和流量控制HOWTO-中文版

    虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。

    TC限制源和目标IP脚本

    资源描述:TC(Traffic Control)脚本实战指南...TC是Linux内核中强大的网络流量控制和整形工具,适用于网络测试、QoS设置及流量管理等多种场景。本教程适合网络管理员、系统工程师以及对Linux网络管理感兴趣的开发者。

    lksctp-rs:Rust 的 Linux 内核 SCTP 低级绑定

    流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看...

    精通LINUX设备驱动程序开发

    311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:...

    基于Linux 的防火墙技术研究

    络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...

    Linux模拟网络丢包与延迟的方法

    netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的

    Linux C 一站式学习

    7.3. 流量控制 37. socket编程 1. 预备知识 1.1. 网络字节序 1.2. socket地址的数据类型及相关函数 2. 基于TCP协议的网络程序 2.1. 最简单的TCP网络程序 2.2. 错误处理与读写控制 2.3. 把client改为交互式输入 2.4. ...

    DarkShell_Linux-Win集群版V2014年

    Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...

Global site tag (gtag.js) - Google Analytics