如何让软件支持扩展功能

简介:
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
微博:weibo.com/glinuxer
QQ技术群:4367710
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
=======================================================
作为程序员的我们,必须保证灵活的设计,才能够应付变化的需求。 但是,当把二进制程序发布给用户以后,用户有了新的需求,如果只能由开发者对程序进行修改,无疑是低效率的。而且有的时候,某些用户的需求,应用并不广泛,开发者不可能为一个用户添加该功能。这时候,如果该程序可以支持用户自定义的扩展功能,无疑是对用户是一大福音。

那么,如何让我们的程序支持用户自定义的扩展功能呢?恩~~,还是从Linux的宝库里面寻找吧。今天选择两个学习对象:1. iptables;2. tc

iptables的扩展实现:为了实现新的扩展,需要在iptables的源代码目录下的extensions目录添加新的功能的代码。iptables的扩展功能框架非常清晰,只需要按照iptables的match结构 xtables_match和target结构xtables_target的定义,实现相应的功能即可。具体的参见 http://blog.chinaunix.net/uid-23069658-id-3230608.html 这篇文章。(该文章的iptable版本较低,数据结构与最近版本的iptables完全不一致) 话说,我之前对于iptable的扩展是一窍不通。后来通过该文章知道了如何扩展iptables。几天的时间,就实现了多个iptables的扩展功能——包括用户态和内核态的代码。由此可见, iptables的扩展架构多么友好。但是这里也有个小问题。这样的扩展方式,需要重新编译iptables的代码,生成新的iptable二进制文件。这与我们心目中理想的扩展还差了一小步,稍微有点难以接受。试想,现在的浏览器大都支持扩展插件,有哪个插件需要重新编译浏览器的?

我们C语言程序员也可以实现这样的功能!让我们学习一下tc的扩展吧。以tc中的filer为例。tc的用户(必须也是程序员呵),可以实现自己的filter的实现,并将生成的so库文件放置在tc的库目录下。那么该扩展功能既可以被tc支持。同时,还要编写一个tc的内核实现模块并加载。这样,新的功能,在不重新编译tc,不重启机器的情况下,就得以支持了。这样就很类似浏览器插件的功能了吧:)也是我们期待的结构。

下面看看tc是如何做到这点的,最直接的方法就是查看tc的代码。
查看函数tc_filter_modify的部分代码:

  1. strncpy(k, *argv, sizeof(k)-1);

  2. = get_filter_kind(k);
  3. argc--; argv++;
tc从命令行参数argv中得到filter的类型并存到k中。接下来通过函数get_filter_kinde得到该类型filter的所有操作函数,主要是parse函数。

接下来进入关键的get_filter_kind的函数:

  1. struct filter_util *get_filter_kind(const char *str)
  2. {
  3.     void *dlh;
  4.     char buf[256];
  5.     struct filter_util *q;
     
     /* 这里去遍历已知的filter列表,通过名字查找对应的filter类型 */
  1.     for (= filter_list; q; q = q->next)
  2.         if (strcmp(q->id, str) == 0)
  3.             return q;

     /*
     这部分代码是tc支持自定义扩展的关键代码 
     没有找到,那么就去tc目录下加载对应名字的动态库
     */
  1.     snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
  2.     dlh = dlopen(buf, RTLD_LAZY);
  3.     if (dlh == NULL) {
  4.         /* 
  5.         如果打开该动态库失败,那么就直接去主程序中寻找。
  6.         这样的情况一般是对于tc自身已支持的filter类型。
  7.         */
  8.         dlh = BODY;
  9.         if (dlh == NULL) {
  10.             dlh = BODY = dlopen(NULL, RTLD_LAZY);
  11.             if (dlh == NULL)
  12.                 goto noexist;
  13.         }
  14.     }

     /* 
     打开动态库后,再根据filter的类型名找到对应的name_filter_util符号 
     该符号实际上即为tc的扩展接口,其为一个结构体,定义了filter的操作函数。
     得到该符号后,tc的用户态部分就已经可以支持新的扩展功能了。
     */
  1.     snprintf(buf, sizeof(buf), "%s_filter_util", str);
  2.     q = dlsym(dlh, buf);
  3.     if (== NULL)
  4.         goto noexist;

  5. reg:
  6.     q->next = filter_list;
  7.     filter_list = q;
  8.     return q;
  9. noexist:
  10.     q = malloc(sizeof(*q));
  11.     if (q) {
  12.         memset(q, 0, sizeof(*q));
  13.         strncpy(q->id, str, 15);
  14.         q->parse_fopt = parse_nofopt;
  15.         q->print_fopt = print_nofopt;
  16.         goto reg;
  17.     }
  18.     return q;
  19. }
在上面的代码中,我已经通过注释的方式,解释了tc如何支持扩展的filter类型。

对比iptables和tc的支持扩展的形式,无疑tc更胜一筹,因为无需重新编译iptables就可以支持新的扩展。这都是依赖于dlopen和dlsym来实现的,在我们自己的项目中,也可以采取同样的方式来支持用户自定义的扩展功能。
目录
相关文章
|
缓存 网络协议 算法
Netty的基础入门(上)
Netty的基础入门(上)
443 1
|
分布式计算 资源调度 Hadoop
HBase表数据的读、写操作与综合操作
HBase表数据的读、写操作与综合操作
232 0
|
iOS开发 MacOS
还在为 iTerm 多窗口操作烦恼?tmux 这款神器轻松帮你解决(下)
粉在之前文章中教过大家如何结合 zsh 让 iterm2 发挥最佳效果。 什么还没有看过?赶紧看下补一下前提知识:收集了这么多实用技巧,帮助你的 iterm2 成为最帅的那个! 上篇文中,阿粉提到每次上线发布的时候,都会打开很多 iTerm 窗口,通过 tab 页拖拽方式让所有窗口可以同时显示。
1234 0
还在为 iTerm 多窗口操作烦恼?tmux 这款神器轻松帮你解决(下)
|
安全 数据安全/隐私保护
阿里云账号开启手机号登录方法
阿里云账号开启手机号验证码登录方法
2731 0
阿里云账号开启手机号登录方法
|
9月前
|
机器学习/深度学习 搜索推荐 API
淘宝/天猫按图搜索(拍立淘)API的深度解析与应用实践
在数字化时代,电商行业迅速发展,个性化、便捷性和高效性成为消费者新需求。淘宝/天猫推出的拍立淘API,利用图像识别技术,提供精准的购物搜索体验。本文深入探讨其原理、优势、应用场景及实现方法,助力电商技术和用户体验提升。
|
XML 搜索推荐 数据格式
资源描述框架的用途及实际应用解析
**RDF(资源描述框架)**是一种用于机器理解网络资源的框架,使用XML编写。它通过URI标识资源,用属性描述资源,便于计算机应用程序处理信息。RDF在语义网上促进信息的确切含义和自动处理,使得网络信息可被整合。RDF语句由资源、属性和属性值组成。RDF文档包括`<rdf:RDF>`根元素和`<rdf:Description>`元素,后者用`about`属性标识资源。RDF还支持容器(如`<Bag>`、`<Seq>`和`<Alt>`)来描述集合。RDFS是RDF的扩展,提供描述类和属性的框架,而达布林核心是一组预定义属性,用于描述文
355 0
|
11月前
|
Android开发
Android实战之如何快速实现自动轮播图
本文介绍了在 Android 中使用 `ViewPager2` 和自定义适配器实现轮播图的方法,包括添加依赖、布局配置、创建适配器及实现自动轮播等步骤。
436 0
|
Go 开发者
Go语言带缓冲通道:异步通信的艺术
Go语言带缓冲通道:异步通信的艺术
147 0
|
算法 网络安全 数据安全/隐私保护
【计算机网络】网络安全 : 公钥分配 ( 公钥使用者 | 公钥分配 | CA 证书格式 | CA 证书吊销 )
【计算机网络】网络安全 : 公钥分配 ( 公钥使用者 | 公钥分配 | CA 证书格式 | CA 证书吊销 )
431 0
|
机器学习/深度学习 人工智能 物联网
快速玩转 Llama2!机器学习 PAI 最佳实践(三)—快速部署WebUI
本实践将采用阿里云机器学习平台PAI-EAS 模块针对 Llama-2-13B-chat 进行部署。PAI-EAS是模型在线服务平台,支持将模型一键部署为在线推理服务或AI-Web应用,具备弹性扩缩的特点,适合需求高性价比模型服务的开发者。
1953 4