如何让软件支持扩展功能

简介:
作者: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来实现的,在我们自己的项目中,也可以采取同样的方式来支持用户自定义的扩展功能。
目录
相关文章
|
8月前
|
安全 编译器 程序员
C++对C的扩展(下)
C++对C的扩展
64 0
|
4月前
|
JavaScript
扩展|2-10
扩展|2-10
|
4月前
|
安全 程序员
分享5款在不同场景提供支持的软件
本文介绍了五款实用的软件工具,涵盖静态分析、文件管理、微信空号检测、软件卸载及跨平台共享等领域。其中包括专为逆向工程设计的Cerbero Suite Advanced、现代化文件管理工具tagLyst Next、智能微信空号检测软件燃精灵、专业卸载工具IObit Uninstaller,以及跨平台共享工具Synergy,助力用户高效完成各类任务。
52 1
|
8月前
|
SQL 消息中间件 数据采集
功能特性
本文介绍日志服务主要的功能。
47 1
|
8月前
|
搜索推荐 安全 定位技术
产品服务功能特性
产品服务功能特性
133 3
|
8月前
|
安全 程序员 编译器
C++对C的扩展(上)
C++对C的扩展
66 0
|
存储 数据采集 监控
|
C语言 C++ 容器
|
存储 C#
扩展按钮图标
扩展按钮图标
460 1
扩展按钮图标
|
监控 Java Spring
扩展JavaMelod支持自定义监控点
扩展JavaMelod支持自定义监控点
218 0
扩展JavaMelod支持自定义监控点