基于Linux的QoS编程接口研究与分析(7)[完]_第1页
基于Linux的QoS编程接口研究与分析(7)[完]_第2页
基于Linux的QoS编程接口研究与分析(7)[完]_第3页
基于Linux的QoS编程接口研究与分析(7)[完]_第4页
基于Linux的QoS编程接口研究与分析(7)[完]_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、文档来源为 :从网络收集整理 .word 版本可编辑 .欢迎下载支持基于 Linux 的 QoS 编程接口研究与分析( 7) 完 第四章Linux的QoS编程接口 4.1 QoS数据通道全局的数据通道的略图见图4.1,灰色部分是 QoS:图4.1网络数据加工数据包的处理过程是:I叩ut Interface 宀Ingress Policing 宀 put Demultiplexing (判断分组是本地使用还是转发)宀Forwarding 宀 Output Queuing 宀 Output Interface。入口的 流量限制(Ingress Policing )和出口 的队列调度(Output

2、Queuing)是由Linux核心的流量控制的代码实现的。入口 的流量限制(Ingress Policing )丢弃不符合规定的分组,确 保进入的各个业务 流分组速率的上限,出口的队列调度( Output Queuing )依据配置实现分组的排 队、丢弃。 以下是分析 Linux 2.4 的代码得出的数据通道, 根据在图 4.1 中的位置,分为 Ingress policing 的数据通道和 Output Queuing 的数据通路,一下分别阐述:4.1.1 Ingress policing 的数据通道Ingress policing 的 QoS 的部分是通过 HOOK 实现的。HOOK 是

3、Linux 实现 netfilter 的重要手段,数据从接收到 转发得通路上有5个HOOK点,每个HOOK点有若干个 挂钩处理函数 :( typedef unsigned int nf_hookfn(unsigned int hooknum,struct sk_buff *skb,const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *);)。 Ingress policing 用 的 是NF_IP_PRE_ROUTING 这个 HOOK 点,其挂钩处理函数用 的是 net/s

4、ched/sch_ingress.c ing_hook()。 Ingress policing 的 数据通道的略图,灰色部分是QoS,黑色部分是 HOOK的处理过程:图4.2 Ingress policing 的总概图1 其中, HOOK 点 NF_IP_PRE_ROUTING :刚刚进入网络层 的数据包通过此点(刚刚进行完版本号,校验和等检测) , 源地址转换在此点进行; NF_IP_LOCAL_IN :经路由查找后, 送往本机的通过此检查点 ,INPUT 包过滤在此点进行; NF_IP_FORWARD :要转发的包通过此检测点 ,FORWORD 包过滤在此点进行; NF_IP_LOCAL_

5、OUT :本机进程发出 的 包通过此检测点, OUTPUT 包过滤在此点进行; NF_IP_POST_ROUTING :所有马上便要通过网络设备出去的 包通过此检测点,内置的目的地址转换功能(包括地址 伪装)在此点进行2 以下数据通路是以 ipv4 为例, ipv6 类似图 4.3 Ingress policing 的数据通道的略图netif_rx()- 数据接收队列队列 - Bottom Half 程序中通过 软中断调用net_rx_action()将硬件层得到的数据传输到 IP层 - ip_rcv() #net/ipv4/ip_input.c 丢弃校验和不正确的 ip 包 - nf_hoo

6、k_slow()#net/core/netfilter.c- nf_iterate()#net/core/netfilter.c- ing_hook() #net/sched/sch_ingress.c-QoS:如果设置了qdisc_ingress,贝U调用 ingress_dequeue(),此处可以对流量进行限制#net/sched/sch_ingress.c-ip_rcv_finish()#net/ipv4/ip_input.c (sch_ingress.c 的 enqueue() 有限流制作用,然而 dequeue() 却是空函数。 ) - 以下路由: ip_route_input()

7、 #net/ipv4/route.c- 如 果 转 发 ip_route_input_slow() #net/ipv4/route.c , 如 果 本 地 处 理 ip_route_input_mc() #net/ipv4/route.c3 为了避免处理长中断服务程序的时候,关中时间过长, Linux 将中断服务程序一分为二,各称作 top half 和 bottom half,前者读取来自设备的数据,保存到预定的缓冲区(队 列),然后通知 bottom half 在适当的时候完成。 4 Ingress 模 块初始化时,把ing_hook()注册为挂钩(HOOK )处理函数5 Qdisc_in

8、gress 不是一个队列,它只有 ingress_enqueue(), 而 ingress_dequeue()只是一个空函数。把ingress_enqueue()理解为一个限流阀门更准确。 4.1.2 Output Queuing 的数据通 路Output Queuing的数据通路略图,灰色部分是 QoS:图4.4 Output Queuing 的数据通路略图这个部分描述转发数据的数据通路,位于路由之后: ip_forward() #net/ipv4/ip_forward.c- ip_forward_finish() #net/ipv4/ip_forward.c- ip_send()#incl

9、ude/net/ip.h-ip_finish_output()#net/ipv4/ip_outp ut.c- ip_finish_output2() #net/ipv4/ip_output.c- neigh_resolve_output()orneigh_connected_output()#net/core/nei ghbour.c-dev_queue_xmit() #net/core/dev.c-QoS:如果有排队方式,那么skb先进入排队q-enqueue(skb,q),然后运行 qdisc_run()#include/net/pkt_sched.h :while (!netif_que

10、ue_stopped(dev) & qdisc_restart(dev)dequeue(q)4.2 Sched的代码结构在net/sched/目录下放着 Linux 目前已经实现的用于路由转发调度的各个算法, 例 如 cbq、tbf 等等。 这个目录下的文件大体上可以分为三个 部分:1) sch*.c: sch_api.c、sch_generic.c 和 sch_atm.c、sch_cbq.c、sch_gred.c、sch_htb.c、sch_csz.c、sch_dsmark.c、sch_fifo.c 、sch_ingress.c、sch_prio.c、sch_red.c、sch_sfq.c、

11、sch_tbf.c、sch_teql.c, 前 2 个提供一些通用的函数,后面几个是模块 化的程序,以注册和注销一个结构体变量 作为模块的初始 化和清除。6 Linux 中,在 INET Socket 层及其以下层次之间数据包的 传递是通过 struct sk_buff 结构完成的。例如对于 sch_tbf.c ,这个文件实现的是令牌桶算法,最后生 成一个 struct Qdisc_ops 的结 构 变 量 tbf_qdisc_ops , 在 模 块 初 始 化 的 时 候 , 注 册 tbf_qdisc_ops , register_qdisc(&tbf_qdisc_ops) ,注册的过程其

12、实就是加入一 个链表的过程, sch_api.c 提 供这个注册的函数。以下是 sch_tbf.c 文件的一部分代码,其余的 sch*.c 的第二部分的 文 件与之类似: struct Qdisc_ops tbf_qdisc_ops = NULL, NULL, tbf, sizeof(struct tbf_sched_data), tbf_enqueue, tbf_dequeue, tbf_requeue, tbf_drop, tbf_init, tbf_reset, tbf_destroy, tbf_change, tbf_dump, ;#ifdef MODULEint init_modul

13、e(void) return register_qdisc(&tbf_qdisc_ops);void cleanup_module(void) unregister_qdisc(&tbf_qdisc_ops); #endifsch*c的第二部分的文件定义了、,有enqueue2) cls*.* : cls_api . c 和 cls_fw.c、 cls_route.c、 cls_rsvp.c、cls_rsvp6.c、cls_tcindex.c、cls_u32.c,cls_rsvp.h。前者提供分类通用的函数,后面几个是模块化的东西,以注册和注销 一个结 构体变量作为模块的初始化和清除。3) e

14、stimator.c和police.c,提供一些通用的函数。 关键数据结构 struct Qdisc_ops struct Qdisc_ops *next;struct Qdisc_class_ops *cl_ops;char idIFNAMSIZ;int priv_size; int (*enqueue)(struct sk_buff *, struct Qdisc *); struct sk_buff* (*dequeue)(struct Qdisc *);int (*requeue)(struct sk_buff *, struct Qdisc *);int (*drop)(struct

15、 Qdisc *); int (*init)(struct Qdisc *, struct rtattr*arg);void (*reset)(struct Qdisc *);void (*destroy)(struct Qdisc *);int (*change)(struct Qdisc *, struct rtattr *arg);int(*dump)(struct Qdisc *, struct sk_buff *);4.3 iprouter2 的代码结构 iproute2 是一个用户空间的程序, 它的功能是解释以 tc8 开头的命令,如果解释成功,把 它 们通过 AF_NETLINK

16、 的 socket 传给 Linux 的内核空间。 iproute2 的代码主要有 include 、ip、lib 、misc、tc 目录组成, misc 的代码量很少, 并且 作用不大, 此处略去。 Include 是 一个包含头文件的目录,这个目录下的头文件会被其他目 录下的*.c文件所用,lib目录定义了一些通用的函数,例如与向 linux 系统传递 tc 参数的方 法:例如 rtnl_talk9, rtnl_send10 等等,此点在第 4 节中有详细的介绍。 Ip 目录 代码主要用于解 释路由的命令,使得流量的控制策略可以 与路由挂钩。 不过这不是本文想要详细讨论的。 tc 目录的代

17、 码是 Tc 的最为主要的部分,解释了流量控制和整形的大部 分命令。 tc 目录的代码分为四个部分, f_*.c、q_*.c、m_*.c、 tc_*.c+tc*.h :1) f_*.c ,解释各种分类器 (filter ),与 sched/cls_*.c 相对应。2) q_*.c ,解释各种队列规范(qdisc).与sched/sch_*.c相 对应。3) m_*.c,这部分就两个文件:m_estimator.c 和 m_police.c,分别对应于 sched/estimator.c 和 sched/police.c 。4) tc_*.c+tc.h,主控文件 tc.c,把解释任务分给tc_q

18、disc.c、tc_filter.c 、 tc_class.c 中的函数。 以下是 tc.c/main() 中的代码: if (argc 1) if (matches(argv1, qdisc) = 0) return do_qdisc(argc-2, argv+2);if (matches(argv1, class) = 0) return do_class(argc-2, argv+2);if (matches(argv1, filter) = 0) return do_filter(argc-2, argv+2);if (matches(argv1, help) = 0) usage()

19、;fprintf(stderr, Object %s is unknown, try tc help.n, argv1);exit(-1);iproute2 提供给命令的详解可见 HMM+02 ,附一提供常用 的命令。 4.4 Sched 与 iproute2 的通信: AF_NETLINKSched 与 iproute2 的通信,是典型的 Linux 内核模块和用户空间 的进程之间的通信,这 种通信一般由 Netlink Socket 来提 供这种双向的通信连接。这种连接由标准的提供给用户进 程的 socket 和提供给内核模块的 API 组成, 用户空间的接 口简单的说就是创建一个 fam

20、ily 为 AF_NETLINK 的 socket,然后使用这个 socket进行通信,自然,用户空间的进程除了 sock_fd = socket(AF_NETLINK ,SOCK_RAW, );是看不到这与其 他的通信方式(例如 AF_INET )有任何的不同; 内核空间 维护一张 link_rtnetlink_table 表 rtnetlinks 。以下结合 iproute2 控制 Linux 的 TC 模块,通过一个例子 分析控制通路,得到用户空间 发送、用户空间接收、内核 发送、内核接收的一些视图。一个命令:tc qdisc add dev ethl这个命令为某个网络接口 eth1 增

21、加一个 qdisc。 命令首先在用户空间被 iproute2 分析:1 )分析 tc: main(int argc, char *argv) 被调用,此函数在 tc/tc.c 中;2) 分析 tc qdisc: do_qdisc(argc-2, argv+2); 被调用,此函数 在 tc/tc_qdisc.c 中;3) 分析 tc qdisc add: tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); 被调用, 此函数在 tc/tc_qdisc.c 中,在这个函数中,将分析完这一行 tc 的命令,各种

22、参数(例如 RTM_NEWQDISC ) 被写到 netlink 包中,并且开始与核心通信。在用户空间中,这个顺序为 rtnl_open rtnl_talk rtnl_close , rtnl_open 的作用是打开一个 AF_NETLINK 的 socket, rtnl_close 的作用是关闭打开的 AF_NETLINK 的 socket, int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, unsigned groups, struct nlmsghdr *answer,int (*junk)(s

23、truct sockaddr_nl *,struct nlmsghdr *n, void *), void *jarg) ,这个函数是 iproute2 与 linux 内核通信的一个库函数, 是属 于用户空间的函数。用户空间通信前的准备: 填充 netlink 包;然后把 netlink 包 发送到内核空间去。详见以下代 码。if (k0) addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); if (est.ewma_log)addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeo

24、f(est); /* 通过这个函数, 所有的参数都被填充进 netlink 的包中 */ if (rtnl_open(&rth, 0) 0) fprintf(stderr, Cannot open rtnetlinkn); exit(1);if (d0) int idx;ll_init_map(&rth);if (idx = ll_name_to_index(d) = 0) fprintf(stderr, Cannot find device %sn, d); exit(1);= idx;if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) 0

25、)/* 在此之前,已经通过 rtnl_open(&rth, 0) 打开一个 socket*/ exit(2);rtnl_close(&rth);(rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) nlmsg_len ; char buf8192;struct msghdr msg = (void*)&nladdr, sizeof(nladdr),&iov, 1 ,NULL,0 ,0,status = sendmsg(rtnl-fd, &msg, 0);iov.iov_base = buf; while (1) status = recvmsg(rt

26、nl-fd, &msg, 0);内核模块的初始化:在 net/sched/sch_api.c 文件中的 void _init pktsched_init (void) 函数 中,初始化了 link_rtnetlink_table 表, link_rtnetlink_table 是一张 struct rtnetlink_link 的表struct rtnetlink_link int (*doit)(struct sk_buff *, struct nlmsghdr*, void *attr); int (*dumpit)(struct sk_buff *, struct netlink_cal

27、lback *cb); ;struct rtnetlink_link 由函数指针 doit 和 dumpit 组成,这张 表可以由需要执行的动作的宏定义 (例如: RTM_NEWQDISC ,RTM_DELQDISC )来索引, 以使得能通 过这张表调动相应的 函数。内核模块从用户空间收到的就 是这些索引和参数,以此调用注册在此表中的函数。 link_p = rtnetlink_linksPF_UNSPEC;/* Setup rtnetlink links. It is made here to avoid exporting large number of public symbols. *

28、/if (link_p) link_pRTM_NEWQDISC-RTM_BASE.doit = tc_modify_qdisc;link_pRTM_DELQDISC-RTM_BASE.doit = tc_get_qdisc; link_pRTM_GETQDISC-RTM_BASE.doit = tc_get_qdisc; link_pRTM_GETQDISC-RTM_BASE.dumpit = tc_dump_qdisc;link_pRTM_NEWTCLASS-RTM_BASE.doit = tc_ctl_tclass; link_pRTM_DELTCLASS-RTM_BASE.doit =

29、tc_ctl_tclass; link_pRTM_GETTCLASS-RTM_BASE.doit = tc_ctl_tclass; link_pRTM_GETTCLASS-RTM_BASE.dumpit = tc_dump_tclass; 下面具体分析一下这个通信的过程: 用户空间:用户空间发送rtnl_talk() #iproute2/lib/libnetlink.c- sendmsg(rtnl-fd,&msg,0) #net/socket.c- sock_sendmsg(sock, &msg_sys, total_len) #net/socket.c- sock-ops-sendmsg(s

30、ock, msg, size, &scm) #net/socket.c ,在 这里,通过函数指针, 调用了 static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, int len,struct scm_cookie *scm) 这在 af_netlink.c 中定义。 netlink_unicast(sk, skb, dst_pid, msg-msg_flags&MSG_DONTWAIT); 或者 netlink_broadcast(sk, skb, dst_pid, dst_groups, GFP_KERNEL)

31、; 用户空间接收 rtnl_talk() #iproute2/lib/libnetlink.c-whilestatus = recvmsg( );#net/socket.c-sock_recvmsg(sock, &msg_sys, total_len, flags); #net/socket.c- sock-ops- recvmsg(sock, msg, size, flags, &scm); #net/socket.c 在这里,通过函数指 针调用了 static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, int

32、len, int flags, struct scm_cookie *scm) ,这在 af_netlink.c 中定 义。 netnetlinkaf_netlink.c 中定义 struct proto_ops netlink_ops = family: PF_NETLINK, release: netlink_release, bind: netlink_bind, connect: netlink_connect, socketpair: sock_no_socketpair, accept: sock_no_accept, getname: netlink_getname, poll:

33、 datagram_poll, ioctl:sock_no_ioctl, listen: sock_no_listen, shutdown: sock_no_shutdown, setsockopt: sock_no_setsockopt, getsockopt:sock_no_getsockopt, sendmsg: netlink_sendmsg, recvmsg: netlink_recvmsg, mmap: sock_no_mmap, sendpage: sock_no_sendpage, ;在内核中: 内核发送 例如 qdisc_notify(skb, n, clid, NULL,

34、q);#net/sched/sch_api.c-rtnetlink_send(skb, pid, RTMGRP_TC, n-nlmsg_flags&NLM_F_ECHO); #net/core/rtnetlink.c- netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL); 和 netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT); 这在 af_netlink.c 中定义。内核接收(关于这个部分,我想还需要修改) void _init rtnetlink_init() #net/core/rtnetlink.c- netlink_ke

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论