部落格:blog.focus-linux.net linuxfocus.blog.chinaunix.net
微網誌:weibo.com/glinuxer
QQ技術群:4367710
本文的copyleft歸[email protected]所有,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文檔的完整性,注明原作者及原連結,嚴禁用于任何商業用途。
=======================================================
作為程式員的我們,必須保證靈活的設計,才能夠應付變化的需求。但是,當把二進制程式釋出給使用者以後,使用者有了新的需求,如果隻能由開發者對程式進行修改,無疑是低效率的。而且有的時候,某些使用者的需求,應用并不廣泛,開發者不可能為一個使用者添加該功能。這時候,如果該程式可以支援使用者自定義的擴充功能,無疑是對使用者是一大福音。
那麼,如何讓我們的程式支援使用者自定義的擴充功能呢?恩~~,還是從Linux的寶庫裡面尋找吧。今天選擇兩個學習對象:1. iptables;2. tc
我們C語言程式員也可以實作這樣的功能!讓我們學習一下tc的擴充吧。以tc中的filer為例。tc的使用者(必須也是程式員呵),可以實作自己的filter的實作,并将生成的so庫檔案放置在tc的庫目錄下。那麼該擴充功能既可以被tc支援。同時,還要編寫一個tc的核心實作子產品并加載。這樣,新的功能,在不重新編譯tc,不重新開機機器的情況下,就得以支援了。這樣就很類似浏覽器插件的功能了吧:)也是我們期待的結構。
下面看看tc是如何做到這點的,最直接的方法就是檢視tc的代碼。
檢視函數tc_filter_modify的部分代碼:
strncpy(k, *argv, sizeof(k)-1);
q = get_filter_kind(k);
argc--; argv++;
tc從指令行參數argv中得到filter的類型并存到k中。接下來通過函數get_filter_kinde得到該類型filter的所有操作函數,主要是parse函數。
接下來進入關鍵的get_filter_kind的函數:
struct filter_util *get_filter_kind(const char *str)
{
void *dlh;
char buf[256];
struct filter_util *q;
/* 這裡去周遊已知的filter清單,通過名字查找對應的filter類型 */
for (q = filter_list; q; q = q->next)
if (strcmp(q->id, str) == 0)
return q;
/*
這部分代碼是tc支援自定義擴充的關鍵代碼
沒有找到,那麼就去tc目錄下加載對應名字的動态庫
*/
snprintf(buf, sizeof(buf), "%s/f_%s.so", get_tc_lib(), str);
dlh = dlopen(buf, RTLD_LAZY);
if (dlh == NULL) {
/*
如果打開該動态庫失敗,那麼就直接去主程式中尋找。
這樣的情況一般是對于tc自身已支援的filter類型。
*/
dlh = BODY;
if (dlh == NULL) {
dlh = BODY = dlopen(NULL, RTLD_LAZY);
if (dlh == NULL)
goto noexist;
}
}
/*
打開動态庫後,再根據filter的類型名找到對應的name_filter_util符号
該符号實際上即為tc的擴充接口,其為一個結構體,定義了filter的操作函數。
得到該符号後,tc的使用者态部分就已經可以支援新的擴充功能了。
snprintf(buf, sizeof(buf), "%s_filter_util", str);
q = dlsym(dlh, buf);
if (q == NULL)
goto noexist;
reg:
q->next = filter_list;
filter_list = q;
return q;
noexist:
q = malloc(sizeof(*q));
if (q) {
memset(q, 0, sizeof(*q));
strncpy(q->id, str, 15);
q->parse_fopt = parse_nofopt;
q->print_fopt = print_nofopt;
goto reg;
}
在上面的代碼中,我已經通過注釋的方式,解釋了tc如何支援擴充的filter類型。
對比iptables和tc的支援擴充的形式,無疑tc更勝一籌,因為無需重新編譯iptables就可以支援新的擴充。這都是依賴于dlopen和dlsym來實作的,在我們自己的項目中,也可以采取同樣的方式來支援使用者自定義的擴充功能。