family=AF_UNSPEC(等同PF_UNSPEC).
它就不具体分析流程了.它根据具体的消息类型和flags做出相应的处理.
req.n.
需要提醒的是在之前ll_init_map初始化的时候的req和这个还是有不同的:
-
在ll_init_map中情况是不同的:
-
int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type,
-
__u32 filt_mask)
-
{
-
struct {
-
struct nlmsghdr nlh;
-
struct ifinfomsg ifm;
-
/* attribute has to be NLMSG aligned */
-
struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO)));
-
__u32 ext_filter_mask;
-
} req;
对于用户配置下发的消息类型为
RTM_NEWQDISC,即在
/* Process one rtnetlink message. */
static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
-
doit = rtnl_get_doit(family, type);
-
if (doit == NULL)
-
return -EOPNOTSUPP;
-
-
return doit(skb, nlh, (void *)&rta_buf[0]);
-
}
tc的配置参数在用户空间解析以rtattr的方式组织,同样到了内核则传递给了rta_buf来处理。(它是rtattr数组)
对于RTM_NEWQDISC则直接调用doit函数即:tc_modify_qdisc,所以我们只需要全力分析这个函数即可
tc_modify_qdisc
1. 找到接口
dev = __dev_get_by_index(net, tcm->tcm_ifindex);
2. 获取管理控制信息
tcm = nlmsg_data(n);
ubuntu14 系统默认的队列规则pfifo_fast
#tc qdisc show
#qdisc pfifo_fast 0: dev eth0 root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 3.查询设备qdisc 对比handle值,如果没找到则创建
qdisc_create,然后绑定通过qdisc_graft ,这个是add qdisc的流程。
那么add class/ filter 呢? 通过tc_ctl_tclass/ tc_ctl_tfilter(
前面说的对应的doit)
在之前我们已经提到过,根据算法名查找qdisc_util,即通过get_qdisc_kind中 dlopen(NULL,XXX)
它开了程序有所的有的库,
然后查找dlsym符号所在的地址。很明显我们之前看到tc在编译连接的时候,例如q_htb.c里的内容。
找到后 ,添加到qdisc_list中. 如果没有则创建一个.
关于dlopen和dlsym这里补充下知识:
我们知道机器可执行的是machine code, 而我们使用的高级语言编程, 并不是利用晦涩的机器码, 而是用human-readable的变量名, 函数名等, 这些名字就是symbolic name. 编译器
在编译时收集symbol信息, 并储存在object file的.symtab和.dynsym中. symbols是linker和debugger所必需的信息, 如果没有symbols, 试想debugger如何能展示给用户调试信息了?
如果没有symbol, 而只有地址(相对于object file的offset), linker如何能够链接多个object file了?
一般地, 生成的可执行文件都是包含symbols, 不过这些信息不是程序执行所必需的, 可以通过strip(Discard symbols from object files)去除
不过对于.dynsym的内容段的内容是程序运行必须的 ,strip也去不掉
查看符号的工具:
debugger/nm/readelf等
Nm test
Readelf -s test
如dlopen("/lib/libmy.so",RTLD_LAZY)。返回对象/lib/libmy.so的句柄。
如果你把pathname输入为NULL,则返回的是一个全局的对象表,包括你在load前的进程镜像表。这种方式很少用,英文解释为:
-
If pathname is NULL, dlopen() provides a handle to the running process's global symbol object. This provides access to the symbols from the original program image file,
-
the dependencies it loaded at startup, plus any objects opened with dlopen() calls using theRTLD_GLOBAL flag. This set of symbols can change dynamically if the
-
application subsequently calls dlopen() using RTLD_GLOBAL.
那么这个全局表是什么呢?我们在查看.systab 和.dysym的时候会看到 “GLOBAL OBJECT” 的标示.
但是在看tc的时候 看到dlopen(null,…); 百思不得其解,为什么它就找到了符号htb_qdisc_util呢?(原因为自己测试程序不成功,但是tc工具是ok的)
看了tc的makefile也是仅仅把q_htb.c连接进去,并没有直接调用,而它却在查找动态库符号的时候找到了。我自己随便写一个验证程序是不行,后来发现编译的时候要指定参数即:
-Wl, -export-dynamic 这个参数会把symbol里的符号都导入到dynsym中.
测试代码我就不贴了,用readelf –a 查看符号的时候,是有的,我们strip后,在.dynsym里已经没有了,所以dlsym里肯定找不到.
-
--export-dynamic
-
When creating a dynamically linked executable, add all symbols to the dynamic symbol table. The
-
dynamic symbol table is the set of symbols which are visible from dynamic objects at run time.
-
If you do not use this option, the dynamic symbol table will normally contain only those symbols
-
which are referenced by some dynamic object mentioned in the link.
-
If you use "dlopen" to load a dynamic object which needs to refer back to the symbols defined by the
-
program, rather than some other dynamic object, then you will probably need to use this option when
-
linking the program itself.
动态库如何编译这里不说了,还有elf的文档资料.