`

Linux内核中流量控制(7)

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

5.7 RED(Random Early Detection queue)

RED算法由Sally Floyd和Van Jacobson提出, 论文为"Random Early Detection Gateways  for Congestion Avoidance", 1993, IEEE/ACM Transactions on Networking.

基本算法:
对新数据包计算平均队列长度:
平均长度avg=(1-W) * avg + W*当前队列长度
 W是参数, 取值为1/(2^Wlog), Wlog可配置, W越小, 平滑能力越强.
 算法中两个阈值: th_min和th_max, 这两个参数可配置
当avg > th_max时,  该新包被丢弃;
当avg < th_min时,  该新包允许通过;
当th_min <= avg <= th_max时, 计算概率:
 Pb = max_P * (avg - th_min)/(th_max-th_min)
然后按此概率丢包, max_P为一小数, 通常为0.01, 0.02等, 一般在算法中通过右移操作来实现:
 max_P = (qth_max-qth_min)>>Plog
Plog为可配置参数

5.7.1 RED操作结构定义

// RED算法参数
struct red_parms
{
 /* Parameters */
// 最小队列长度
 u32  qth_min; /* Min avg length threshold: A scaled */
// 最大队列长度
 u32  qth_max; /* Max avg length threshold: A scaled */
// 最大休眠时间
 u32  Scell_max;
// 保存随机掩码
 u32  Rmask;  /* Cached random mask, see red_rmask */
//
 u8  Scell_log;
// Wlog, Plog参数含义如上所示
 u8  Wlog;  /* log(W)  */
 u8  Plog;  /* random number bits */
// 256个元素
 u8  Stab[RED_STAB_SIZE];
 /* Variables */
// 以下的参数是在处理过程中会改变的参数
// 从上次随机数产生时的处理的数据包数
 int  qcount;  /* Number of packets since last random
        number generation */
// 缓存的随机数
 u32  qR;  /* Cached random number */
// 平均队列长度
 unsigned long qavg;  /* Average queue length: A scaled */
// 当前休眠起始时间
 psched_time_t qidlestart; /* Start of current idle period */
};

// RED私有数据
struct red_sched_data
{
// 最大队列长度, 这是硬限制
 u32   limit;  /* HARD maximal queue length */
// 标志
 unsigned char  flags;
// RED算法参数
 struct red_parms parms;
// RED统计值
 struct red_stats stats;
 struct Qdisc  *qdisc;
};

// RED流控操作结构
static struct Qdisc_ops red_qdisc_ops = {
 .id  = "red",
 .priv_size = sizeof(struct red_sched_data),
 .cl_ops  = &red_class_ops,
 .enqueue = red_enqueue,
 .dequeue = red_dequeue,
 .requeue = red_requeue,
 .drop  = red_drop,
 .init  = red_init,
 .reset  = red_reset,
 .destroy = red_destroy,
 .change  = red_change,
 .dump  = red_dump,
 .dump_stats = red_dump_stats,
 .owner  = THIS_MODULE,
};

// RED类别操作结构
static struct Qdisc_class_ops red_class_ops = {
 .graft  = red_graft,
 .leaf  = red_leaf,
 .get  = red_get,
 .put  = red_put,
 .change  = red_change_class,
 .delete  = red_delete,
 .walk  = red_walk,
 .tcf_chain = red_find_tcf,
 .dump  = red_dump_class,
};
 
5.7.2 RED一些基本操作函数

在include/net/red.h中定义

// 返回Plog对应RED掩码, 和网络掩码不同,RED掩码值是从低位开始算的
// 掩码值位2^Plog-1, , Plog超过31后就和31相同
static inline u32 red_rmask(u8 Plog)
{
 return Plog < 32 ? ((1 << Plog) - 1) : ~0UL;
}

// 设置RED参数, 平均长度阈值的最大最小值等
static inline void red_set_parms(struct red_parms *p,
     u32 qth_min, u32 qth_max, u8 Wlog, u8 Plog,
     u8 Scell_log, u8 *stab)
{
 /* Reset average queue length, the value is strictly bound
  * to the parameters below, reseting hurts a bit but leaving
  * it might result in an unreasonable qavg for a while. --TGR
  */
 p->qavg  = 0;
// 队列元素统计
 p->qcount = -1;
// 内部平均长度阈值的最大最小值为设置值的2^Wlog倍
 p->qth_min = qth_min << Wlog;
 p->qth_max = qth_max << Wlog;
 p->Wlog  = Wlog;
 p->Plog  = Plog;
// 随机掩码
 p->Rmask = red_rmask(Plog);
 p->Scell_log = Scell_log;
// 最大休眠时间
 p->Scell_max = (255 << Scell_log);
// stab
 memcpy(p->Stab, stab, sizeof(p->Stab));
}

// 算法是否在休眠状态, 也就是看qidlestart是否为0, qidlestart非0表示正在休眠
static inline int red_is_idling(struct red_parms *p)
{
 return !PSCHED_IS_PASTPERFECT(p->qidlestart);
}

// RED休眠, 将p->qidlestart设置为当前时间
static inline void red_start_of_idle_period(struct red_parms *p)
{
 PSCHED_GET_TIME(p->qidlestart);
}
// RED停止休眠, 将p->qidlestart设置清零
static inline void red_end_of_idle_period(struct red_parms *p)
{
 PSCHED_SET_PASTPERFECT(p->qidlestart);
}

// RED算法重新启动
static inline void red_restart(struct red_parms *p)
{
// RED数值清零,
 red_end_of_idle_period(p);
 p->qavg = 0;
 p->qcount = -1;
}

// 从休眠恢复后重新计算队列平均值
static inline unsigned long red_calc_qavg_from_idle_time(struct red_parms *p)
{
 psched_time_t now;
 long us_idle;
 int  shift;
// 获取当前时间
 PSCHED_GET_TIME(now);
// 计算当前时间与休眠时的时间差, 也就是计算休眠了多少时间
// p->Scell_max是休眠时间上限
 us_idle = PSCHED_TDIFF_SAFE(now, p->qidlestart, p->Scell_max);
 /*
  * The problem: ideally, average length queue recalcultion should
  * be done over constant clock intervals. This is too expensive, so
  * that the calculation is driven by outgoing packets.
  * When the queue is idle we have to model this clock by hand.
  *
  * SF+VJ proposed to "generate":
  *
  * m = idletime / (average_pkt_size / bandwidth)
  *
  * dummy packets as a burst after idle time, i.e.
  *
  *  p->qavg *= (1-W)^m
  *
  * This is an apparently overcomplicated solution (f.e. we have to
  * precompute a table to make this calculation in reasonable time)
  * I believe that a simpler model may be used here,
  * but it is field for experiments.
  */
// 根据休眠数和Scell_log计算索引值获取stab数组中的偏移值
 shift = p->Stab[(us_idle >> p->Scell_log) & RED_STAB_MASK];
 if (shift)
// 偏移值非0, 当前平均队列值左移后返回
  return p->qavg >> shift;
 else {
  /* Approximate initial part of exponent with linear function:
   *
   *  (1-W)^m ~= 1-mW + ...
   *
   * Seems, it is the best solution to
   * problem of too coarse exponent tabulation.
   */
// 重新计算休眠时间
  us_idle = (p->qavg * (u64)us_idle) >> p->Scell_log;
// 如果休眠时间数值小于队列长度一半
  if (us_idle < (p->qavg >> 1))
// 返回队列长度减休眠时间
   return p->qavg - us_idle;
  else
// 否则返回队列长度的一半
   return p->qavg >> 1;
 }
}

// 非休眠情况下计算队列平均值
static inline unsigned long red_calc_qavg_no_idle_time(struct red_parms *p,
             unsigned int backlog)
{
 /*
  * NOTE: p->qavg is fixed point number with point at Wlog.
  * The formula below is equvalent to floating point
  * version:
  *
  *  qavg = qavg*(1-W) + backlog*W;
  *
  * --ANK (980924)
  */
 return p->qavg + (backlog - (p->qavg >> p->Wlog));
}

// RED计算滑动队列平均值
static inline unsigned long red_calc_qavg(struct red_parms *p,
       unsigned int backlog)
{
// 分活动状态和非活动状态, 分别计算
 if (!red_is_idling(p))
  return red_calc_qavg_no_idle_time(p, backlog);
 else
  return red_calc_qavg_from_idle_time(p);
}

// 生成RED随机数
static inline u32 red_random(struct red_parms *p)
{
 return net_random() & p->Rmask;
}

// 概率随机决定是否丢包
static inline int red_mark_probability(struct red_parms *p, unsigned long qavg)
{
 /* The formula used below causes questions.
    OK. qR is random number in the interval 0..Rmask
    i.e. 0..(2^Plog). If we used floating point
    arithmetics, it would be: (2^Plog)*rnd_num,
    where rnd_num is less 1.
    Taking into account, that qavg have fixed
    point at Wlog, and Plog is related to max_P by
    max_P = (qth_max-qth_min)/2^Plog; two lines
    below have the following floating point equivalent:
    max_P*(qavg - qth_min)/(qth_max-qth_min) < rnd/qcount
    Any questions? --ANK (980924)
  */
// 根据当前队列平均值计算出一个值是否小于随机数qR来实现概率丢包
 return !(((qavg - p->qth_min) >> p->Wlog) * p->qcount < p->qR);
}
enum {
 RED_BELOW_MIN_THRESH,
 RED_BETWEEN_TRESH,
 RED_ABOVE_MAX_TRESH,
};

// 将当前队列平均值与阈值进行比较
static inline int red_cmp_thresh(struct red_parms *p, unsigned long qavg)
{
// 小于低阈值
 if (qavg < p->qth_min)
  return RED_BELOW_MIN_THRESH;
// 不小于高阈值
 else if (qavg >= p->qth_max)
  return RED_ABOVE_MAX_TRESH;
 else
// 高低阈值之间
  return RED_BETWEEN_TRESH;
}
enum {
// 放行
 RED_DONT_MARK,
// 根据概率标记
 RED_PROB_MARK,
// 标记
 RED_HARD_MARK,
};

// RED动作判定
static inline int red_action(struct red_parms *p, unsigned long qavg)
{
// 将当前平均队列值与阈值进行比较
 switch (red_cmp_thresh(p, qavg)) {
  case RED_BELOW_MIN_THRESH:
// 低于低阈值, 放行
   p->qcount = -1;
   return RED_DONT_MARK;
  case RED_BETWEEN_TRESH:
// 高低之间, 按概率标记
   if (++p->qcount) {
// 是否根据概率标记
    if (red_mark_probability(p, qavg)) {
// 标记
     p->qcount = 0;
     p->qR = red_random(p);
     return RED_PROB_MARK;
    }
   } else
    p->qR = red_random(p);
// 不标记
   return RED_DONT_MARK;
  case RED_ABOVE_MAX_TRESH:
// 超过高阈值, 直接标记
   p->qcount = -1;
   return RED_HARD_MARK;
 }
 BUG();
 return RED_DONT_MARK;
}

5.7.3 初始化

static int red_init(struct Qdisc* sch, struct rtattr *opt)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 内部Qdisc初始化为noop_qdisc
 q->qdisc = &noop_qdisc;
// 调用change函数进行初始化
 return red_change(sch, opt);
}

5.7.4 参数修改

static int red_change(struct Qdisc *sch, struct rtattr *opt)
{
 struct red_sched_data *q = qdisc_priv(sch);
 struct rtattr *tb[TCA_RED_MAX];
 struct tc_red_qopt *ctl;
 struct Qdisc *child = NULL;
// 检查数据范围是否合法
 if (opt == NULL || rtattr_parse_nested(tb, TCA_RED_MAX, opt))
  return -EINVAL;
 if (tb[TCA_RED_PARMS-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_RED_PARMS-1]) < sizeof(*ctl) ||
     tb[TCA_RED_STAB-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_RED_STAB-1]) < RED_STAB_SIZE)
  return -EINVAL;
 ctl = RTA_DATA(tb[TCA_RED_PARMS-1]);
// 如果流量限制值有效, 建立RED流控的内部缺省流控结构, 为一BFIFO流控结构
 if (ctl->limit > 0) {
  child = red_create_dflt(sch->dev, ctl->limit);
  if (child == NULL)
   return -ENOMEM;
 }
 sch_tree_lock(sch);
// 设置RED流控结构标志和流量限制参数
 q->flags = ctl->flags;
 q->limit = ctl->limit;
// 对内部流控赋值
 if (child)
  qdisc_destroy(xchg(&q->qdisc, child));
// 设置RED流控算法参数
 red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
     ctl->Plog, ctl->Scell_log,
     RTA_DATA(tb[TCA_RED_STAB-1]));
// 如果队列空, RED进入休眠状态
 if (skb_queue_empty(&sch->q))
  red_end_of_idle_period(&q->parms);
 sch_tree_unlock(sch);
 return 0;
}

// 建立缺省的RED内部Qdisc
static struct Qdisc *red_create_dflt(struct net_device *dev, u32 limit)
{
// 内部Qdisc使用的是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) {
// 填写rtattr结构, 实际有效数据就是流量限制值limit
   rta->rta_type = RTM_NEWQDISC;
   rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt));
   ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
// 调用bfifo的修改函数设置bfifo流控结构的limit参数
   ret = q->ops->change(q, rta);
   kfree(rta);
   if (ret == 0)
    return q;
  }
  qdisc_destroy(q);
 }
 return NULL;
}

5.7.5 入队

static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// RED内部流控节点: bfifo
 struct Qdisc *child = q->qdisc;
 int ret;
// 计算队列滑动平均值
 q->parms.qavg = red_calc_qavg(&q->parms, child->qstats.backlog);
// 如果在休眠, 停止休眠
 if (red_is_idling(&q->parms))
  red_end_of_idle_period(&q->parms);

// 根据队列平均值获取判定结果
 switch (red_action(&q->parms, q->parms.qavg)) {
// 通过
  case RED_DONT_MARK:
   break;
// 概率标记
  case RED_PROB_MARK:
   sch->qstats.overlimits++;
// 如果没用ECN拥塞标志, 丢包
   if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) {
    q->stats.prob_drop++;
    goto congestion_drop;
   }
// 概率标记增加,允许入队
   q->stats.prob_mark++;
   break;
// 直接标记
  case RED_HARD_MARK:
// overlimits增加
   sch->qstats.overlimits++;
// 如果RED设置HARDDROP标志或没使用ECN, 丢包
   if (red_use_harddrop(q) || !red_use_ecn(q) ||
       !INET_ECN_set_ce(skb)) {
    q->stats.forced_drop++;
    goto congestion_drop;
   }
// forced_mask增加, 允许入队
   q->stats.forced_mark++;
   break;
 }
// 调度内部流控的入队函数
 ret = child->enqueue(skb, child);
// 根据入队是否成功更新相关统计
 if (likely(ret == NET_XMIT_SUCCESS)) {
  sch->bstats.bytes += skb->len;
  sch->bstats.packets++;
  sch->q.qlen++;
 } else {
  q->stats.pdrop++;
  sch->qstats.drops++;
 }
 return ret;
congestion_drop:
// 拥塞丢包处理
 qdisc_drop(skb, sch);
 return NET_XMIT_CN;
}

5.7.6 重入队

static int red_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 内部流控
 struct Qdisc *child = q->qdisc;
 int ret;
// 如果在休眠, 停止休眠
 if (red_is_idling(&q->parms))
  red_end_of_idle_period(&q->parms);
// 使用内部流控的重入队函数
 ret = child->ops->requeue(skb, child);
// 成功的话更新统计数据
 if (likely(ret == NET_XMIT_SUCCESS)) {
  sch->qstats.requeues++;
  sch->q.qlen++;
 }
 return ret;
}

5.7.7 出队

static struct sk_buff * red_dequeue(struct Qdisc* sch)
{
 struct sk_buff *skb;
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 内部流控
 struct Qdisc *child = q->qdisc;
// 调用内部流控的出队函数
 skb = child->dequeue(child);
// 如果出队成功, RED队列长度减1, 返回数据包
 if (skb)
  sch->q.qlen--;
 else if (!red_is_idling(&q->parms))
// 否则队列空, RED进入休眠状态
  red_start_of_idle_period(&q->parms);
 return skb;
}

5.7.8 丢包

static unsigned int red_drop(struct Qdisc* sch)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 内部流控
 struct Qdisc *child = q->qdisc;
 unsigned int len;
// 如果内部流控结构定义的丢包函数, 执行该丢包函数, 更新统计返回
 if (child->ops->drop && (len = child->ops->drop(child)) > 0) {
  q->stats.other++;
  sch->qstats.drops++;
  sch->q.qlen--;
  return len;
 }
// 否则使RED进入休眠状态
 if (!red_is_idling(&q->parms))
  red_start_of_idle_period(&q->parms);
 return 0;
}
 
5.7.9 复位

// 就是内部流控的复位函数, RED队列长度清零
static void red_reset(struct Qdisc* sch)
{
 struct red_sched_data *q = qdisc_priv(sch);
 qdisc_reset(q->qdisc);
 sch->q.qlen = 0;
 red_restart(&q->parms);
}

5.7.10 释放

// 就是内部流控的释放函数
static void red_destroy(struct Qdisc *sch)
{
 struct red_sched_data *q = qdisc_priv(sch);
 qdisc_destroy(q->qdisc);
}

5.7.11 输出参数

static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
 struct rtattr *opts = NULL;
// 填写RED参数结构
 struct tc_red_qopt opt = {
  .limit  = q->limit,
  .flags  = q->flags,
  .qth_min = q->parms.qth_min >> q->parms.Wlog,
  .qth_max = q->parms.qth_max >> q->parms.Wlog,
  .Wlog  = q->parms.Wlog,
  .Plog  = q->parms.Plog,
  .Scell_log = q->parms.Scell_log,
 };
// 将参数拷贝到skb数据区
 opts = RTA_NEST(skb, TCA_OPTIONS);
 RTA_PUT(skb, TCA_RED_PARMS, sizeof(opt), &opt);
// 发送数据包
 return RTA_NEST_END(skb, opts);
rtattr_failure:
 return RTA_NEST_CANCEL(skb, opts);
}

5.7.12 输出统计值

static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 构造标准TC统计结构, 填写相关参数
 struct tc_red_xstats st = {
// 提早丢包数: 概率丢和强迫丢
  .early = q->stats.prob_drop + q->stats.forced_drop,
// 丢包数
  .pdrop = q->stats.pdrop,
// 其他
  .other = q->stats.other,
// 被标记的包数
  .marked = q->stats.prob_mark + q->stats.forced_mark,
 };
// 返回
 return gnet_stats_copy_app(d, &st, sizeof(st));
}

5.7.13 RED类别操作

// 输出分类
static int red_dump_class(struct Qdisc *sch, unsigned long cl,
     struct sk_buff *skb, struct tcmsg *tcm)
{
 struct red_sched_data *q = qdisc_priv(sch);
 if (cl != 1)
  return -ENOENT;
// 设置tcm参数:
// handle或1
 tcm->tcm_handle |= TC_H_MIN(1);
// 信息为内部流控handle
 tcm->tcm_info = q->qdisc->handle;
 return 0;
}

// 嫁接, 增加叶子qdisc
static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
       struct Qdisc **old)
{
// RED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 如果没定义新流控, 用noop_qdisc
 if (new == NULL)
  new = &noop_qdisc;
 sch_tree_lock(sch);
// 将当前RED内部流控和新流控结构指针对换
 *old = xchg(&q->qdisc, new);
// 复位老流控结构
 qdisc_reset(*old);
// 流控队列长度清零
 sch->q.qlen = 0;
 sch_tree_unlock(sch);
 return 0;
}

// 获取叶子流控节点
static struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg)
{
 struct red_sched_data *q = qdisc_priv(sch);
// 返回RED内部流控: bfifo
 return q->qdisc;
}

// 引用计数
static unsigned long red_get(struct Qdisc *sch, u32 classid)
{
 return 1;
}
// 释放计数,空函数
static void red_put(struct Qdisc *sch, unsigned long arg)
{
 return;
}

// 更改类别, 无定义
static int red_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
       struct rtattr **tca, unsigned long *arg)
{
 return -ENOSYS;
}
// 删除节点, 无定义
static int red_delete(struct Qdisc *sch, unsigned long cl)
{
 return -ENOSYS;
}

// 遍历
static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker)
{
// 其实也说不上遍历, 因为就只执行一次
 if (!walker->stop) {
  if (walker->count >= walker->skip)
   if (walker->fn(sch, 1, walker) < 0) {
    walker->stop = 1;
    return;
   }
  walker->count++;
 }
}
// 查找分类过滤规则, 空函数
static struct tcf_proto **red_find_tcf(struct Qdisc *sch, unsigned long cl)
{
 return NULL;
}

...... 待续 ......
分享到:
评论

相关推荐

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

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

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

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

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

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

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

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

    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的范例...

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

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

    编写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,隧道的配置与其他部分完全集成了。

    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 ,需要至少两个条件,一个是内核中的

    DarkShell_Linux-Win集群版V2014年

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

    ip route2 源码 第二代网络工具

    如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...

    Linux C 一站式学习

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

Global site tag (gtag.js) - Google Analytics