1 Net_cls
1.1 原理
net_cls子系统是用来控制进程使用网络资源的,它并不直接控制网络读写,而是给网络包打上一个标记,具体的网络包控制由tc机制来处理。
net_cls实现的基本的思想就是将控制组和内核现有的网络包分类和调度的机制相关联。net_cls通过给控制组分配一个类标识符(classid)来指定该控制组的数据包将被分到哪个traffic class(net_cls只支持可分类的qdsic队列规则)。数据包在发送的时候会根据添加到设备qdisc上的cgroup filter将数据包分到与其classid相符的traffic class队列中,再由设备上设置的具体qdsic来控制数据包的发送,以达到控制网络资源使用的目的。
创建一个挂有net_cls的cgroup后,在其下会生有个名为net_cls.classid(默认初始值为0)的文件,通过这个文件指定该组进程相关的数据包进入哪个traffic class。通过向文件写入形如0xAAAABBBB的十六进制值(AAAA为主处理号,BBBB为次处理号,读取该值是以十进制显示),设置cgroup的classid后,再进行相应的tc配置,添加符合classid的traffic class,并使用cgroup filter。这样当cgroup中的进程需要使用网络接口发送数据包的时候,则会按照接口上classid相应的tc策略控制进程对网络资源的使用。
整体形式如下:
1.2 实现
net_cls中对应的核心数据结构为
structcgroup_cls_state{
struct cgroup_subsys_state css; //对应cgroup子系统状态描述符css
u32 classid; //用用于记录该cgroup的classid
};
cgroup_cls_state记录了该组设置的classid,对net_cls.classid读写时操作的就是这个值。在发送数据包过程中,内核会将进程所在的cgroup的classid存储在skb->sk->sk_classid中。
为了能和tc机制结合,net_cls模块添加了一个名为cgroup的filter,其相应的操作函数结构如下:
staticstruct tcf_proto_ops cls_cgroup_ops __read_mostly = {
.kind = "cgroup", //filter名称
.init = cls_cgroup_init, //初始化filter的id、root等相关信息
.change = cls_cgroup_change, //修改filter参数
.classify = cls_cgroup_classify, //对数据包进行分类
.destroy = cls_cgroup_destroy, //释放filter的root数据
.get = cls_cgroup_get, //获取引用
.put = cls_cgroup_put, //减少引用
.delete = cls_cgroup_delete, //删除
.walk = cls_cgroup_walk, //遍历filter参数
.dump = cls_cgroup_dump, //用于输出信息
.owner = THIS_MODULE, //模块指针
};
其中定义了cgroup filter的相关的操作。在初始化net_cls模块时会注册cgroup filter,内核将其加入全局变量tcf_proto_base链表中:
register_tcf_proto_ops(&cls_cgroup_ops)。
tc命令是通过netlink与内核进行通信的,当使用tc filter add命令设置cgroup filter时,内核会调用已经注册的tc_ctl_tfilter函数进行处理。
static int __init tc_filter_init(void)
{
rtnl_register(PF_UNSPEC,RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL);
rtnl_register(PF_UNSPEC,RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL);
rtnl_register(PF_UNSPEC,RTM_GETTFILTER, tc_ctl_tfilter,
tc_dump_tfilter, NULL);
return0;
}
tc_ctl_tfilter函数中遍历tcf_proto_base链表,根据tc命令中指定的cgroup过滤器,找到kind为cgroup的filter操做函数结构,并将其加入指定设备使用的qdsic的分类规则链表中。当发送一个数据的时候,在设备发送函数dev_queue_xmit中会根据设备上设置的qdisc,对数据包进行分类排队。分类时内核会遍历qdsic的分类规则链表,并调用相应的分类函数对数据包进行分类,这样之前添加的cgroup filter的分类函数就会被调用了。cgroup filter分类关键就是获取设置的classid,将该classid返回给qdisc进行数据包分类排队:
cgroupfilter先获取当前进程所在cgroup的classid:
rcu_read_lock();
classid =task_cls_state(current)->classid;
rcu_read_unlock();
如果是在软中断上下文中,为了确保结果的正确性,则直接获取存储在唉skb->sk中的classid:
if(in_serving_softirq()) {
/* If there is an sk_classid we'lluse that. */
if (!skb->sk)
return -1;
classid =skb->sk->sk_classid;
}
最终将结果保存,由cgroup filter绑定的qdisc根据获得的classid将数据包入队,进行具体的流量控制:
res->classid= classid;
res->class= 0;
1.3 使用
使用net_cls子系统控制网络资源的使用,主要有以下三步:
1. 创建绑定net_cls的cgroup
2. 设置该cgroup的classid
3. 配置符合classid的tc策略配置(与tc流量控制紧密相关,可参考tc相关知识:http://www.netren.org/index.php/linux-tc.html)
例子:
1.创建net_cls子系统cgroup:
#mkdir /cgroup/net_cls
#mount –t cgroup –o net_cls net_cls/cgroup/net_cls
#mkdir /cgroup/net_cls/test
2.给cgroup test设置classid(1:1)
#echo 0x100001 >/cgroup/net_cls/cgroupa/net_cls.classid
#cat/cgroup/net_cls/cgroupa/net_cls.classid
65537
3.配置tc
#在设备eth0上创建添加队列规则qdisc,此处为htb流量控制
#tc qdisc add dev eth0 root handle 1:htb
# tc -s qdisc show dev eth0
qdischtb 1: root refcnt 2 r2q 10 default 0 direct_packets_stat 245
Sent27514 bytes 245 pkt (dropped 0, overlimits 0 requeues 0)
backlog0b 0p requeues 0
#在qdisc下建立一个类class,classid为1:1,与写入cgroup中的相对应
#tc class add dev eth0 parent 1:classid 1:1 htb rate 40mbit
# tc -s class show dev eth0
classhtb 1:1 root prio 0 rate 40000Kbit ceil 40000Kbit burst 1600b cburst 1600b
Sent0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
rate0bit 0pps backlog 0b 0p requeues 0
lended:0 borrowed: 0 giants: 0
tokens:5000 ctokens: 5000
#添加cgroup过滤器
#tc filter add dev eth0 parent 1:protocol ip prio 10 handle 1: cgroup
# tc -s filter show dev eth0
filterparent 1: protocol ip pref 10 cgroup handle 0x1
看看限制的效果:
# time scp iperf-2.0.4.tar.gz 128.5.130.21:/home/w00227741/
real 0m1.894s
user 0m0.020s
sys 0m0.004s
# time cgexec -g net_cls:test scp iperf-2.0.4.tar.gz 128.5.130.21:/home/w00227741/
real 0m2.429s
user 0m0.020s
sys 0m0.004s
#将可用带宽有4mbit改为1mbit
# tc class change dev br0 parent 10:classid 1:1 htb rate 1mbit
# time cgexec -g net_cls:test scp iperf-2.0.4.tar.gz 128.5.130.21:/home/w00227741/
real 0m4.220s
user 0m0.020s
sys 0m0.000s
可见后两种情况比第一种要来的慢,且分配的带宽越少越慢。