Linux下動態(tài)鏈接庫*.so的編譯與使用(二)

程序清理

有一個C程序登澜,用于實現(xiàn)算法和數(shù)據(jù)結構 (比如棧和相關的操作)她按。在同一個程序中牛隅,還有用于測試的main()函數(shù),結構體定義酌泰,函數(shù)原型,typedef等等匕累。
這樣的做法非常不“環(huán)绷晟玻”。算法的實際運用和算法的實現(xiàn)混在一起欢嘿。如果我想要重復使用之前的源程序衰琐,必須進行許多改動,并且重新編譯炼蹦。最好的解決方案是實現(xiàn)模塊化: 只保留純粹的算法實現(xiàn)羡宙,分離頭文件,并編譯一個庫(library)掐隐。每次需要使用庫的時候(比如使用棧數(shù)據(jù)結構)狗热,就在程序中include頭文件,連接庫虑省。這樣匿刮,不需要每次都改動源程序。
如何在UNIX環(huán)境中創(chuàng)建共享庫 (shared library)探颈。UNIX下熟丸,共享庫以so為后綴(shared object)。共享庫與Windows下的DLL類似伪节,是在程序運行時動態(tài)連接光羞。多個進程可以連接同一個共享庫绩鸣。

/* By Vamei */
/* use single-linked list to implement stack */
#include <stdio.h>
#include <stdlib.h>

typedef struct node *position;
typedef int ElementTP;

// point to the  head node of the list
typedef struct node *STACK;
 
struct node {
    ElementTP element;
    position next;
};

STACK init_stack(void);
void delete_stack(STACK);
ElementTP top(STACK);
void push(STACK, ElementTP);
ElementTP pop(STACK);
int is_null(STACK);

void main(void)
{
    ElementTP a;
    int i;
    STACK sk;
    sk = init_stack();
    push(sk, 1);
    push(sk, 2);
    push(sk, 8);
    printf("Stack is null? %d\n", is_null(sk));
    for (i=0; i<3; i++) {
        a = pop(sk);
        printf("pop: %d\n", a);
    }

    printf("Stack is null? %d\n", is_null(sk));    
    delete_stack(sk);
}

/*
 * initiate the stack
 * malloc the head node.
 * Head node doesn't store valid data
 * head->next is the top node
 */
STACK init_stack(void)
{
    position np;
    STACK    sk;
    np = (position) malloc(sizeof(struct node));
    np->next     = NULL;  // sk->next is the top node
    sk = np; 
    return sk;
}

/* pop out all elements 
 * and then delete head node
 */
void delete_stack(STACK sk)
{
    while(!is_null(sk)) {
        pop(sk);
    }
    free(sk);
}
/* 
 * View the top frame
 */
ElementTP top(STACK sk)
{
    return (sk->next->element);
}

/*
 * push a value into the stack
 */
void push(STACK sk, ElementTP value) 
{
    position np, oldTop;
    oldTop = sk->next;    

    np = (position) malloc(sizeof(struct node));
    np->element  = value;
    np->next     = sk->next;

    sk->next     = np; 
}

/* 
 * pop out the top value
 */
ElementTP pop(STACK sk)
{
    ElementTP element;
    position top, newTop;
    if (is_null(sk)) {
        printf("pop() on an empty stack");
        exit(1);
    } 
    else {
        top      = sk->next;
        element  = top->element;     
        newTop   = top->next;
        sk->next     = newTop;
        free(top);
        return element;
    } 
}

/* check whether a stack is empty*/
int is_null(STACK sk)
{
    return (sk->next == NULL);
}

上面的main()部分是用于測試,不屬于功能模塊纱兑,在創(chuàng)建庫的時候應該去掉全闷。

程序中的一些聲明,會被重復利用萍启。比如:

typedef struct node *position;
typedef int ElementTP;

// point to the  head node of the list
typedef struct node *STACK;
 
struct node {
    ElementTP element;
    position next;
};

STACK init_stack(void);
void delete_stack(STACK);
ElementTP top(STACK);
void push(STACK, ElementTP);
ElementTP pop(STACK);
int is_null(STACK);

這一段程序聲明了一些結構體和指針总珠,以及棧操作的函數(shù)原型。當我們其他程序中調(diào)用庫時 (比如創(chuàng)建一個棧勘纯,或者執(zhí)行pop操作)局服,同樣需要寫這些聲明。我們把這些在實際調(diào)用中需要的聲明保存到一個頭文件mystack.h驳遵。在實際調(diào)用的程序中淫奔,可以簡單的include該頭文件,避免了每次都寫這些聲明語句的麻煩堤结。

經(jīng)過清理后的C程序為mystack.c:

/* By Vamei */
/* use single-linked list to implement stack */
#include <stdio.h>
#include <stdlib.h>
#include "mystack.h"


/*
 * initiate the stack
 * malloc the head node.
 * Head node doesn't store valid data
 * head->next is the top node
 */
STACK init_stack(void)
{
    position np;
    STACK    sk;
    np = (position) malloc(sizeof(struct node));
    np->next     = NULL;  // sk->next is the top node
    sk = np; 
    return sk;
}

/* pop out all elements 
 * and then delete head node
 */
void delete_stack(STACK sk)
{
    while(!is_null(sk)) {
        pop(sk);
    }
    free(sk);
}
/* 
 * View the top frame
 */
ElementTP top(STACK sk)
{
    return (sk->next->element);
}

/*
 * push a value into the stack
 */
void push(STACK sk, ElementTP value) 
{
    position np, oldTop;
    oldTop = sk->next;    

    np = (position) malloc(sizeof(struct node));
    np->element  = value;
    np->next     = sk->next;

    sk->next     = np; 
}

/* 
 * pop out the top value
 */
ElementTP pop(STACK sk)
{
    ElementTP element;
    position top, newTop;
    if (is_null(sk)) {
        printf("pop() on an empty stack");
        exit(1);
    } 
    else {
        top      = sk->next;
        element  = top->element;     
        newTop   = top->next;
        sk->next     = newTop;
        free(top);
        return element;
    } 
}

/* check whether a stack is empty*/
int is_null(STACK sk)
{
    return (sk->next == NULL);
}

#include "..."; 語句將首先在工作目錄尋找相應文件唆迁。如果使用gcc時,增加-I選項竞穷,將在-I提供的路徑中尋找唐责。

制作.so文件

我們的目標是制作共享庫,即.so文件瘾带。
首先鼠哥,編譯stack.c:
$gcc -c -fPIC -o mystack.o mystack.c
-c表示只編譯(compile),而不連接看政。-o選項用于說明輸出(output)文件名朴恳。gcc將生成一個目標(object)文件mystack.o。
注意-fPIC選項允蚣。PIC指Position Independent Code于颖。共享庫要求有此選項,以便實現(xiàn)動態(tài)連接(dynamic linking)嚷兔。
生成共享庫:
$gcc -shared -o libmystack.so mystack.o
庫文件以lib開始森渐。共享庫文件以.so為后綴。-shared表示生成一個共享庫谴垫。
這樣章母,共享庫就完成了。.so文件和.h文件都位于當前工作路徑(.)翩剪。

使用共享庫

我們編寫一個test.c乳怎,來實際調(diào)用共享庫:

#include <stdio.h>
#include "mystack.h"

/*
 * call functions in mystack library
 */
void main(void)
{
    ElementTP a;
    int i;
    STACK sk;
    sk = init_stack();
    push(sk, 1);
    push(sk, 2);
    push(sk, 8);
    printf("Stack is null? %d\n", is_null(sk));
    for (i=0; i<3; i++) {
        a = pop(sk);
        printf("pop: %d\n", a);
    }

    printf("Stack is null? %d\n", is_null(sk));    
    delete_stack(sk);
}

編譯上述程序。編譯器需要知道.h文件位置。
對于#include "..."蚪缀,編譯器會在當前路徑搜索.h文件秫逝。你也可以使用-I選項提供額外的搜索路徑,比如-I/home/vamei/test询枚。
對于#include <...>违帆,編譯器會在默認include搜索路徑中尋找。
編譯器還需要知道我們用了哪個庫文件金蜀,在gcc中:
使用-l選項說明庫文件的名字刷后。這里,我們將使用-lmystack (即libmystack庫文件)渊抄。
使用-L選項說明庫文件所在的路徑尝胆。這里,我們使用-L. (即.路徑)护桦。
如果沒有提供-L選項含衔,gcc將在默認庫文件搜索路徑中尋找。
你可以使用下面的命令二庵,來獲知自己電腦上的include默認搜索路徑:
$gcc -print-prog-name=cc1-v
獲知庫默認搜索路徑:
$gcc -print-search-dirs
我們所需的.h和.so文件都在當前路徑贪染,因此,使用如下命令編譯:
$gcc -o test test.c -lmystack -L.
將生成test可執(zhí)行文件催享。
$./test執(zhí)行程序

運行程序

./test: error while loading shared libraries: libmystack.so: cannot open shared object file: No such file or directory
這是因為操作系統(tǒng)無法找到庫杭隙。libmystack.so位于當前路徑,位于庫文件的默認路徑之外睡陪。盡管我們在編譯時(compile time)提供了.so文件的位置寺渗,但這個信息并沒有寫入test可執(zhí)行文件(runtime)±计龋可以使用下面命令測試:
$ldd test
ldd用于顯示可執(zhí)行文件所依賴的庫。顯示:

linux-vdso.so.1 =>  (0x00007fff31dff000)
    libmystack.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fca30de7000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fca311cb000)

這說明test可執(zhí)行文件無法找到它所需的libmystack.so庫文件炬称。
為了解決上面的問題汁果,我們可以將.so文件放入默認搜索路徑中。但有時玲躯,特別是多用戶環(huán)境下据德,我們不享有在默認搜索路徑寫入的權限。

一個解決方案是設置LD_LIBRARY_PATH環(huán)境變量跷车。比如:
$export LD_LIBRARY_PATH=.
這樣棘利,可執(zhí)行文件執(zhí)行時,操作系統(tǒng)將在先在LD_LIBRARY_PATH下搜索庫文件朽缴,再到默認路徑中搜索善玫。環(huán)境變量的壞處是,它會影響所有的可執(zhí)行程序密强。如果我們在編譯其他程序時茅郎,如果我們不小心蜗元,很可能導致其他可執(zhí)行文件無法運行。因此系冗,LD_LIBRARY_PATH環(huán)境變量多用于測試奕扣。
另一個解決方案,即提供-rpath選項掌敬,將搜索路徑信息寫入test文件(rpath代表runtime path)惯豆。這樣就不需要設置環(huán)境變量。這樣做的壞處是奔害,如果庫文件移動位置楷兽,我們需要重新編譯test。使用如下命令編譯test.c:
$gcc -g -o test test.c -lmystack -L. -Wl,-rpath=.
-Wl表示舀武,-rpath選項是傳遞給連接器(linker)拄养。
test順利執(zhí)行的結果為:

Stack is null? 0
pop: 8
pop: 2
pop: 1
Stack is null? 1
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市银舱,隨后出現(xiàn)的幾起案子瘪匿,更是在濱河造成了極大的恐慌,老刑警劉巖寻馏,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棋弥,死亡現(xiàn)場離奇詭異,居然都是意外死亡诚欠,警方通過查閱死者的電腦和手機顽染,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轰绵,“玉大人粉寞,你說我怎么就攤上這事∽笄唬” “怎么了唧垦?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長液样。 經(jīng)常有香客問我振亮,道長,這世上最難降的妖魔是什么鞭莽? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任坊秸,我火速辦了婚禮,結果婚禮上澎怒,老公的妹妹穿的比我還像新娘褒搔。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布站超。 她就那樣靜靜地躺著荸恕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪死相。 梳的紋絲不亂的頭發(fā)上融求,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音算撮,去河邊找鬼生宛。 笑死,一個胖子當著我的面吹牛肮柜,可吹牛的內(nèi)容都是我干的陷舅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼审洞,長吁一口氣:“原來是場噩夢啊……” “哼莱睁!你這毒婦竟也來了?” 一聲冷哼從身側響起芒澜,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤仰剿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痴晦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南吮,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年誊酌,在試婚紗的時候發(fā)現(xiàn)自己被綠了部凑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡碧浊,死狀恐怖涂邀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情箱锐,我是刑警寧澤必孤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站瑞躺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏兴想。R本人自食惡果不足惜幢哨,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫂便。 院中可真熱鬧捞镰,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凸丸,卻和暖如春拷邢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屎慢。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工瞭稼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腻惠。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓环肘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親集灌。 傳聞我的和親對象是個殘疾皇子悔雹,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容