天天看點

如何讓軟體支援擴充功能

作者:[email protected]

部落格: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來實作的,在我們自己的項目中,也可以采取同樣的方式來支援使用者自定義的擴充功能。

繼續閱讀