• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評論 - 101, 引用 - 0
            數(shù)據(jù)加載中……

            QT精彩實例分析

            qt的畫圖機制為顯示器和打印提供了統(tǒng)一的api接口,主要有3個大類QPainter(提供畫圖操作的基本接口和操作) QPaintDevice(提供畫圖的設(shè)備也就是你的圖畫在那個地方)和QPaintEngine(為QPainter和QPaintDevice提供內(nèi)部使用的抽象接口定義,一般不會用到的).

            QPainter-->QPaintEngine---->QPaintDevice

            其中QPaintDevice有QWidget 、QImage、QPixmap、QPicture、QGLWidget、QPrinter、QGLPixleBuffer

            http://qt-apps.org/

            http://www.qtcentre.org/content/


            今天寫個簡單的時鐘的程序,秒鐘、分鐘,時鐘能夠自動的正確的顯示現(xiàn)在的時間,但是不能夠修改。這個程序非常簡單需要的基本知識是:

            1.Qpainter類

            2.QTimer

            步驟:

            1.先新建一個widget

            2.重新定義void paintEvent(QPainter *painter)

            3.定義一個畫圖的函數(shù) void draw(QPainter *painter)

            main.cpp

            #include <QtGui/QApplication>

            #include "timer.h"
            #include<QTextCodec>
            int main(int argc, char *argv[])
            {
                QApplication a(argc, argv);
                QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
                Timer w;
                w.show();
                return a.exec();
            }

            timer.h

            #ifndef TIMER_H

            #define TIMER_H
            #include <QWidget>
            namespace Ui {
                class Timer;
            }
            class Timer : public QWidget
            {
                Q_OBJECT
            public:
                explicit Timer(QWidget *parent = 0);
                ~Timer();
                void paintEvent(QPaintEvent *);
                void draw(QPainter *painter);
            private:
                Ui::Timer *ui;
            };
            #endif // TIMER_H

            timer.cpp

            #include "timer.h"

            #include "ui_timer.h"
            #include<QtGui>
            Timer::Timer(QWidget *parent) :
                QWidget(parent),
                ui(new Ui::Timer)
            {
                ui->setupUi(this);
                QTimer *timer = new QTimer;//新建一個定時器來定時刷新圖
                connect(timer,SIGNAL(timeout()),this,SLOT(update()));
                timer->start(1000);//每隔1秒鐘重新繪制一下
                setWindowTitle(tr("深海的小魚兒"));//定義widget的標(biāo)題
                resize(200,200);//初始固定大小為200x200
            }
            Timer::~Timer()
            {
                delete ui;
            }
            void Timer::paintEvent(QPaintEvent *)
            {
                QPainter painter(this); //定義一個畫筆
                painter.setRenderHint(QPainter::Antialiasing);//開啟反轉(zhuǎn)使你畫的圖比較光滑
                int side = qMin(width(), height());//取長寬的最小值來作為你放大和縮小的標(biāo)準(zhǔn)值(也就是說你的
                painter.translate(width() / 2, height() / 2);//長寬是一樣的,這樣縮放時不會變形。移到中心點,這時width/height=200
                painter.scale(side / 200.0, side / 200.0);
                draw(&painter);//開始畫圖
            }
            void Timer::draw(QPainter *painter)
            {
                static const QPoint hourHand[3] = {
                    QPoint(7, 8),
                    QPoint(-7, 8),
                    QPoint(0, -40)
                };//定義時針的圖像
                static const QPoint minuteHand[3] = {
                    QPoint(7, 8),
                    QPoint(-7, 8),
                    QPoint(0, -65)
                };//定義分針的圖像
                static const QPoint secondHand[3] = {
                    QPoint(7, 8),
                    QPoint(-7, 8),
                    QPoint(0, -85)
                };//定義秒針的圖像
                QColor hourColor(127, 0, 127);//定義時針的顏色
                QColor minuteColor(0, 127, 127, 191);//定義分針的顏色
                QColor secondColor(0, 0,255);//定義秒針的顏色
                QTime time = QTime::currentTime();//取系統(tǒng)的時間
                painter->setPen(Qt::NoPen);//開始畫秒針的在系統(tǒng)時間的位置
                painter->setBrush(secondColor);
                painter->save();
                painter->rotate(time.second()*6);//360/60=6
                painter->drawConvexPolygon(secondHand, 3);
                painter->restore();
                painter->setPen(Qt::NoPen);//開始畫分針的在系統(tǒng)時間的位置
                painter->setBrush(hourColor);
                painter->save();
                painter->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
                painter->drawConvexPolygon(hourHand, 3);
                painter->restore();
                painter->setPen(hourColor);
                for (int i = 0; i < 12; ++i)
                {
                    painter->drawLine(88, 0, 96, 0);
                    painter->rotate(30.0);
                }
                painter->setPen(Qt::NoPen);//開始畫時針的在系統(tǒng)時間的位置
                painter->setBrush(minuteColor);
                painter->save();
                painter->rotate(6.0 * (time.minute() + time.second() / 60.0));
                painter->drawConvexPolygon(minuteHand, 3);
                painter->restore();
                painter->setPen(minuteColor);
                for (int j = 0; j < 60; ++j)
                {
                    if ((j % 5) != 0)
                        painter->drawLine(92, 0, 96, 0);
                    painter->rotate(6.0);
                }

            posted @ 2012-12-07 22:58 tqsheng 閱讀(812) | 評論 (0)編輯 收藏

            elf文件格式學(xué)習(xí)總結(jié)

                編譯器將一個源文件生成目標(biāo)文件時,會在目標(biāo)文件中生成符號表和重定位表。

                

                符號表包含在文件中定義的全局符號以及在文件中引用的外部符號(外部函數(shù)或變量)。

                重定位表告訴鏈接器在哪些位置要進行重定位操作。

                

                編譯器生成的目標(biāo)文件在文件的開始處會有一個elf頭,描繪了整個文件的組織結(jié)構(gòu)。它還包括很多節(jié)(section)。這些節(jié)有的是系統(tǒng)定義好的,有些是用戶在文件在通過.section命令自定義的,鏈接器會將多個輸入目標(biāo)文件中的相同的節(jié)合并。

                

                鏈接器對編譯生成的目標(biāo)文件進行鏈接時,A. 首先進行符號解析,找出外部符號在哪定義。如果外部符號在一個靜態(tài)庫中定義,則直接將對應(yīng)的定義代碼復(fù)制到最終生成的目標(biāo)文件中。B. 接著鏈接器進行符號重定位。編譯器在生成目標(biāo)文件時,通常使用從零開始的相對地址,而在鏈接過程中,鏈接器從一個指定的地址開始,根據(jù)輸入目標(biāo)文件的順序,以段(segment)為單位將它們拼裝起來。其中每個段可以包括很多個節(jié)(section)。除了目標(biāo)文件的拼裝,重定位過程中還完成了下面兩個任務(wù):一是生成最終的符號表,二是對代碼段(.text)中的某些位置進行修改,要修改的位置由編譯器生成的重定位表指出。

                

                鏈接過程中還會生成兩個表:got表和plt表。

                

                got表中每一項都是本運行模塊要引用的全局變量或函數(shù)的地址,可以用got表來間接引用全局變量。函數(shù)也可以把got表的首地址作為一個基準(zhǔn),用相對該基準(zhǔn)的偏移量來(直接)引用靜態(tài)函數(shù)。由于動態(tài)鏈接器(ld-linux.so)不會把運行模塊(*.so)加載到固定地址,在不同進程的地址空間中各運行模塊的絕對地址、相對地址都不同。這種不同反映到got表上,就是每個進程的每個運行模塊都有獨立的got表,所以進程間不能共享got表(內(nèi)容)。

                

                plt表中每一項都是一小段匯編代碼,對應(yīng)于本運行模塊要引用的每一個全局函數(shù)。當(dāng)鏈接器發(fā)現(xiàn)某個符號引用是位于其它共享目標(biāo)文件(動態(tài)連接庫*.so)中的一個全局函數(shù)時,就在文件的plt表里創(chuàng)建一個項目,以便將來重定位。

                

                鏈接生成的目標(biāo)文件在文件開頭也有一個elf頭號,描繪了整個文件的組織結(jié)構(gòu),這個文件中會有多個段(segment),每個段都由相應(yīng)的節(jié)(section)拼裝而成。

                對由鏈接器鏈接生成的可執(zhí)行目標(biāo)文件進行加載運行時,內(nèi)核首先讀取elf頭。根據(jù)頭部數(shù)據(jù)指示分別讀入各種數(shù)據(jù)結(jié)構(gòu),找出可加載的段并調(diào)用mmap()函數(shù)將其加載到內(nèi)存。內(nèi)核找到標(biāo)記為PT_INTERP的段,這個段對應(yīng)著動態(tài)鏈接器的名稱,然后加載動態(tài)鏈接器(linux中通常是/lib/ld-linux.so.2)。接著內(nèi)核將控制權(quán)交給動態(tài)鏈接器。動態(tài)鏈接器檢查程序?qū)ν獠课募ü蚕韼欤┑囊蕾囆裕⒃谛枰獣r對其進行加載。之后動態(tài)鏈接器開始對程序中的外部引用(即全局變量/函數(shù))進行重定位,即定位出程序其引用的外部變量/函數(shù)的真實內(nèi)存地址。R_386_GLOB_DAT類型的重定位項目涉及到got表。R_386_JMP_SLOT類型的重定位項目涉及到plt表。動態(tài)鏈接還有一個延遲(lazy)特性,即真正引用時才進行重定位(環(huán)境變量LD_BIND_NOW為空值NULL時)。接下來動態(tài)鏈接器執(zhí)行elf文件中標(biāo)記為.init節(jié)的代碼,進行程序運行的初始化。最后動態(tài)鏈接器把控制權(quán)交給程序,從elf頭中定義的入口處開始執(zhí)行程序。

            posted @ 2012-12-07 22:43 tqsheng 閱讀(318) | 評論 (0)編輯 收藏

            Qt學(xué)習(xí)筆記

            老規(guī)矩,先下載一個qt-x11-opensource版本,configure,make,make install一切都照說明來。
            默認(rèn)安裝目錄:/usr/local/Trolltech/Qt-4.5.0/

            編寫環(huán)境變量設(shè)置腳本,以免鏈到系統(tǒng)的舊Qt庫。
            qt_pc.env 腳本內(nèi)容如下:

            export QMAKESPEC=linux-g++
            export QTDIR=/usr/local/Trolltech/Qt-4.5.0
            export PKG_CONFIG_PATH=/usr/local/lib:${QTDIR}/lib/pkgconfig
            export LD_LIBRARY_PATH=/usr/local/lib:${QTDIR}/lib
            export PATH=${QTDIR}/bin:$PATH

            第一行指定qmake使用的spec,意思是使用/usr/local/Trolltech/Qt-4.5.0/mkspecs/linux-g++這個目錄中的文件指定一些變量。如果是嵌入式平臺可以看到里面連用什么編譯器都指定了,比如/usr/local/Trolltech/Qt-4.5.0/mkspecs/qws/linux-arm-g++目錄。
            第二行聲明的QT的安裝目錄。
            第三行是為了pkg-config能夠找到qt庫
            第四行是為了運行時能找到qt庫
            第五行保證運行qmake,rcc等工具時是運行的最新的,而不是系統(tǒng)上的舊版本。

            1.建個目錄存放工程:
                  mkdir qt_study
                  cd qt_study
                  mkdir src
            2.創(chuàng)建工程pro文件:
                  vi qt_study.pro
                  文件內(nèi)容如下:
                  #模板app,生成獨立運行的程序
                  TEMPLATE = app
                  #生成的可執(zhí)行文件存放目錄
                  DESTDIR = bin
                  #生成的可執(zhí)行文件名稱
                  TARGET = qt_study
                  #源文件、臨時文件存放目錄
                  DEPENDPATH += src
                  INCLUDEPATH +=
                  MOC_DIR = .tmp/moc
                  RCC_DIR = .tmp/rcc
                  UI_DIR = .tmp/ui
                  OBJECTS_DIR = .tmp/obj
                  #打開警告、支持大文件
                  CONFIG += qt warn_on largefile
                  #指定代碼中國際化需要翻譯的字符串存儲文件。可指定多個
                  TRANSLATIONS = qt_study_zh_CN.ts
                  #資源文件,一些圖片,翻譯文本等。編譯后會與可執(zhí)行文件集成在一個文件中
                  RESOURCES += qt_study.qrc
            # 源代碼
            HEADERS += src/mainwindow.h

            SOURCES += src/main.cpp \
                       src/mainwindow.cpp

            3.創(chuàng)建資源文件
            vi qt_study.qrc
            文件內(nèi)容如下:
            <!DOCTYPE RCC><RCC version="1.0">
            <qresource>
            <file>./qt_study_zh_CN.qm</file>
            </qresource>
            </RCC>
            這兒只包含了一個翻譯文本文件,qm文件是由ts文件生成的,后面會有說明。
            4.編寫代碼:
            mkdir src
            cd src
            編寫main.cpp,mainwindow.cpp,mainwindow.h。
            main.cpp:
            #include "mainwindow.h"

            void installTranslator()
            {
                    //獲取系統(tǒng)的local name。
                    //在我的系統(tǒng)上執(zhí)行l(wèi)ocale,得到的是"zh_CN.UTF-8"。這兒name()會得到zh_CN
                    QString locale = QLocale::system().name();

                    //新創(chuàng)建一個QTranslator對象,不能是棧對象。
                    QTranslator* translator = new QTranslator;
                    //從內(nèi)置資源中l(wèi)oad語言包。:/表示是內(nèi)置資源;qt_study_zh_CN.qm會被load
                    translator->load( QString(":/qt_study_" + locale) );
                    //安裝語言包。程序啟動后的界面就是zh_CN的了。
                    qApp->installTranslator(translator);
            }

            int main( int argc, char** argv )
            {
                    //初始化資源。就是qt_study.qrc文件去掉擴展名
                    Q_INIT_RESOURCE( qt_study );

                    QApplication app( argc, argv );
                    //安裝translator
                    installTranslator();
                    //創(chuàng)建主界面
                    qtStudyMainWindow w;
                    w.show();

                    return app.exec();
            }
            mainwindow.h 及 mainwindow.cpp見附件
            5.翻譯文本:
            生成ts文件:
            lupdate -pro qt_study.pro
            翻譯文本
            linguist qt_study_zh_CN.ts
            生成qm文件
            lrelease qt_study_zh_CN.ts
            6.編譯:
            qmake
            make
            7.執(zhí)行:
            如果系統(tǒng)內(nèi)沒有中文字體,需要把ttf字體復(fù)制到/usr/local/Trolltech/Qt-4.5.0/lib/fonts/
            ./bin/qt_study
            可以看到正常翻譯結(jié)果出現(xiàn)。


            posted @ 2012-12-07 22:41 tqsheng 閱讀(321) | 評論 (0)編輯 收藏

            Windows下靜態(tài)編譯QT程序

            Windows下編寫的QT程序拿到別的機器上運行時總是要打包DLL,編譯成靜態(tài)程序更方便一些。 

            首先要編譯一個靜態(tài)的Qt庫,因為ms的連接器需要.lib庫,而qt自帶的是.a庫,所以要下載代碼自己編譯了。 
            編譯前先修改一下spec:(下面是對于vs2008,如果是2005,則去找win32-msvc2005) 
            mkspecs ----> win32-msvc2008 ----> qmake.conf 
            修改QMAKE_CFLAGS_RELEASE = -O2 -MD 為QMAKE_CFLAGS_RELEASE = -O2 -MT 
            修改QMAKE_CFLAGS_DEBUG 為  = -Zi -MTd 
            修改這兒是因為Qt configure時的static選項對核心庫無效,核心庫仍然是動態(tài)鏈接的。另外-Zi也可以去掉,不生成pdb文件,如果不調(diào)試Qt內(nèi)部就沒必要要。 
            執(zhí)行: 
            configure -release -static -fast -qt-sql-odbc -qt-sql-sqlite -no-webkit 
            nmake 
            等待個1,2小時的編譯,生成一堆的.lib文件。 
            如果需要webkit就不要加-no-webkit參數(shù) 

            在vs中修改工程的屬性: 
            配置屬性 ----> C/C++ ----> 代碼生成 ---->運行時庫 ----> /MT or /MTd 

            這樣最終生成的程序就是靜態(tài)程序了,拿到?jīng)]有QT庫的機器上也可以運行了。

            posted @ 2012-12-07 22:37 tqsheng 閱讀(499) | 評論 (0)編輯 收藏

            dll和so文件區(qū)別與構(gòu)成

            動態(tài)鏈接,在可執(zhí)行文件裝載時或運行時,由操作系統(tǒng)的裝載程序加載庫。大多數(shù)操作系統(tǒng)將解析外部引用(比如庫)作為加載過程的一部分。在這些系統(tǒng)上,可執(zhí)行文件包含一個叫做import   directory的表,該表的每一項包含一個庫的名字。根據(jù)表中記錄的名字,裝載程序在硬盤上搜索需要的庫,然后將其加載到內(nèi)存中預(yù)先不確定的位置,之后根據(jù)加載庫后確定的庫的地址更新可執(zhí)行程序。可執(zhí)行程序根據(jù)更新后的庫信息調(diào)用庫中的函數(shù)或引用庫中的數(shù)據(jù)。這種類型的動態(tài)加載成為裝載時加載   ,被包括Windows和Linux的大多數(shù)系統(tǒng)采用。裝載程序在加載應(yīng)用軟件時要完成的最復(fù)雜的工作之一就是加載時鏈接。  
               
              其他操作系統(tǒng)可能在運行時解析引用。在這些系統(tǒng)上,可執(zhí)行程序調(diào)用操作系統(tǒng)API,將庫的名字,函數(shù)在庫中的編號和函數(shù)參數(shù)一同傳遞。操作系統(tǒng)負責(zé)立即解析然后代表應(yīng)用調(diào)用合適的函數(shù)。這種動態(tài)鏈接叫做運行時鏈接   。因為每個調(diào)用都會有系統(tǒng)開銷,運行時鏈接要慢得多,對應(yīng)用的性能有負面影響。現(xiàn)代操作系統(tǒng)已經(jīng)很少使用運行時鏈接。  
               
              可以動態(tài)鏈接的庫,在Windows上是dynamic   link   library   (DLL),在UNIX或Linux上是Shared   Library。庫文件是預(yù)先編譯鏈接好的可執(zhí)行文件,存儲在計算機的硬盤上。大多數(shù)情況下,同一時間多個應(yīng)用可以使用一個庫的同一份拷貝,操作系統(tǒng)不需要加載這個庫的多個實例。  
               
              Windows   和   Linux   的加載時鏈接是由操作系統(tǒng)來完成的,格式在不同的系統(tǒng)下有不同的區(qū)別,但是原理還是一樣的。
            linux下文件的類型是不依賴于其后綴名的,但一般來講:
            .o,是目標(biāo)文件,相當(dāng)于windows中的.obj文件
            .so 為共享庫,是shared object,用于動態(tài)連接的,和dll差不多
            .a為靜態(tài)庫,是好多個.o合在一起,用于靜態(tài)連接
            .la為libtool自動生成的一些共享庫,vi編輯查看,主要記錄了一些配置信息。可以用如下命令查看*.la文件的格式   $file *.la
                  *.la: ASCII English text
            所以可以用vi來查看其內(nèi)容。
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            創(chuàng)建.a庫文件和.o庫文件:
            [yufei@localhost perl_c2]$ pwd
            /home/yufei/perl_c2
            [yufei@localhost perl_c2]$ cat mylib.c
            #include <stdio.h>
            #include <string.h>
            void hello(){
                    printf("success call from perl to c library\n");
            }
            [yufei@localhost perl_c2]$ cat mylib.h
            extern void hello();
            [yufei@localhost perl_c2]$ gcc -c mylib.c
            [yufei@localhost perl_c2]$ dir
            mylib.c  mylib.h  mylib.o
            [yufei@localhost perl_c2]$ ar -r mylib.a mylib.o
            ar: 正在創(chuàng)建 mylib.a
            [yufei@localhost perl_c2]$ dir
            mylib.a  mylib.c  mylib.h  mylib.o
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
            動態(tài)鏈接庫*.so的編譯與使用- -
                                                  
            動態(tài)庫*.so在linux下用c和c++編程時經(jīng)常會碰到,最近在網(wǎng)站找了幾篇文章介紹動態(tài)庫的編譯和鏈接,總算搞懂了這個之前一直不太了解得東東,這里做個筆記,也為其它正為動態(tài)庫鏈接庫而苦惱的兄弟們提供一點幫助。
            1、動態(tài)庫的編譯
            下面通過一個例子來介紹如何生成一個動態(tài)庫。這里有一個頭文件:so_test.h,三個.c文件:test_a.c、test_b.c、test_c.c,我們將這幾個文件編譯成一個動態(tài)庫:libtest.so。
            so_test.h:
            #include <stdio.h>
            #include <stdlib.h>
            void test_a();
            void test_b();
            void test_c();
            test_a.c:
            #include "so_test.h"
            void test_a()
            {
                printf("this is in test_a...\n");
            }
            test_b.c:
            #include "so_test.h"
            void test_b()
            {
                printf("this is in test_b...\n");
            }
            test_c.c:
            #include "so_test.h"
            void test_c()
            {
                printf("this is in test_c...\n");
            }
            將這幾個文件編譯成一個動態(tài)庫:libtest.so
            $ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
            2、動態(tài)庫的鏈接
            在1、中,我們已經(jīng)成功生成了一個自己的動態(tài)鏈接庫libtest.so,下面我們通過一個程序來調(diào)用這個庫里的函數(shù)。程序的源文件為:test.c。
            test.c:
            #include "so_test.h"
            int main()
            {
                test_a();
                test_b();
                test_c();
                return 0;
            }
            l         將test.c與動態(tài)庫libtest.so鏈接生成執(zhí)行文件test:
            $ gcc test.c -L. -ltest -o test
            l         測試是否動態(tài)連接,如果列出libtest.so,那么應(yīng)該是連接正常了
            $ ldd test
            l         執(zhí)行test,可以看到它是如何調(diào)用動態(tài)庫中的函數(shù)的。
            3、編譯參數(shù)解析
            最主要的是GCC命令行的一個選項:
                      -shared 該選項指定生成動態(tài)連接庫(讓連接器生成T類型的導(dǎo)出符號表,有時候也生成弱連接W類型的導(dǎo)出符號),不用該標(biāo)志外部程序無法連接。相當(dāng)于一個可執(zhí)行文件
            l         -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關(guān)的所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
            l         -L.:表示要連接的庫在當(dāng)前目錄中
            l         -ltest:編譯器查找動態(tài)連接庫時有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱
            l         LD_LIBRARY_PATH:這個環(huán)境變量指示動態(tài)連接器可以裝載動態(tài)庫的路徑。
            l         當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權(quán)限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
            4、注意
                   調(diào)用動態(tài)庫的時候有幾個問題會經(jīng)常碰到,有時,明明已經(jīng)將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過 “-L”參數(shù)引導(dǎo),并指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態(tài)庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。
            makefile里面怎么正確的編譯和連接生成.so庫文件,然后又是在其他程序的makefile里面如何編譯和連接才能調(diào)用這個庫文件的函數(shù)????
            答:
                   你需要告訴動態(tài)鏈接器、加載器ld.so在哪里才能找到這個共享庫,可以設(shè)置環(huán)境變量把庫的路徑添加到庫目錄/lib和/usr/lib,LD_LIBRARY_PATH=$(pwd),這種方法采用命令行方法不太方便,一種替代方法
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注釋^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            LD_LIBRARY_PATH可以在/etc/profile還是 ~/.profile還是 ./bash_profile里設(shè)置,或者.bashrc里,
            改完后運行source /etc/profile或 . /etc/profile
            更好的辦法是添入/etc/ld.so.conf, 然后執(zhí)行 /sbin/ldconfig
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注釋^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            是把庫路徑添加到/etc/ld.so.conf,然后以root身份運行l(wèi)dconfig
                  也可以在連接的時候指定文件路徑和名稱 -I  -L.
                  GCC=gcc
            CFLAGS=-Wall -ggdb -fPIC
            #CFLAGS=
            all: libfunc test
            libfunc:func.o func1.o
                    $(GCC) -shared -Wl,-soname,libfunc.so.1 -o libfunc.so.1.1 $<
                    ln -sf libfunc.so.1.1 libfunc.so.1
                    ln -sf libfunc.so.1 libfunc.so
            ***********************************************注釋************************************************
            ln -s是用來創(chuàng)建軟鏈接,也就相當(dāng)于windows中的快捷方式,在當(dāng)前目錄中創(chuàng)建上一級目錄中的文件ttt的命名為ttt2軟鏈接的命令是ln -s ../ttt ttt2,如果原文件也就是ttt文件刪除的話,ttt2也變成了空文件。
            ln -d是用來創(chuàng)建硬鏈接,也就相當(dāng)于windows中文件的副本,當(dāng)原文件刪除的時候,并不影響“副本”的內(nèi)容。
            編譯目標(biāo)文件時使用gcc的-fPIC選項,產(chǎn)生與位置無關(guān)的代碼并能被加載到任何地址:
            gcc –fPIC –g –c liberr.c –o liberr.o
            使用gcc的-shared和-soname選項;
            使用gcc的-Wl選項把參數(shù)傳遞給連接器ld;
            使用gcc的-l選項顯示的連接C庫,以保證可以得到所需的啟動(startup)代碼,從而避免程序在使用不同的,可能不兼容版本的C庫的系統(tǒng)上不能啟動執(zhí)行。
            gcc –g –shared –Wl,-soname,liberr.so –o liberr.so.1.0.0 liberr.o –lc
            建立相應(yīng)的符號連接:
            ln –s liberr.so.1.0.0 liberr.so.1;
            ln –s liberr.so.1.0.0 liberr.so;
            在MAKEFILE中:
            $@
                表示規(guī)則中的目標(biāo)文件集。在模式規(guī)則中,如果有多個目標(biāo),那么,"$@"就是匹配于目標(biāo)中模式定義的集合。
            $%
                僅當(dāng)目標(biāo)是函數(shù)庫文件中,表示規(guī)則中的目標(biāo)成員名。例如,如果一個目標(biāo)是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是 "foo.a"。如果目標(biāo)不是函數(shù)庫文件(Unix下是[.a],Windows下是[.lib]),那么,其值為空。
            $<
                依賴目標(biāo)中的第一個目標(biāo)名字。如果依賴目標(biāo)是以模式(即"%")定義的,那么"$<"將是符合模式的一系列的文件集。注意,其是一個一個取出來的。
            $?
                所有比目標(biāo)新的依賴目標(biāo)的集合。以空格分隔。
            $^
                所有的依賴目標(biāo)的集合。以空格分隔。如果在依賴目標(biāo)中有多個重復(fù)的,那個這個變量會去除重復(fù)的依賴目標(biāo),只保留一份。
            *********************************************注釋***********************************************************************
            test: test.o libfunc
                    $(GCC) -o test test.o -L. -lfunc
            %.o:%.c
                    $(GCC) -c $(CFLAGS) -o $@ $<
            clean:
                    rm -fr *.o
                    rm -fr *.so*
                    rm -fr test
            要生成.so文件,cc要帶-shared 參數(shù);要調(diào)用.so的文件,比如libfunc.so,可以在cc命令最后加上-lfunc,還要視情況加上 -L/usr/xxx 指出libfunc.so的路徑;這樣,在你要編譯的源文件中就可以調(diào)用libfunc.so這個庫文件的函數(shù).
                   前面的都說的差不多了,最后提醒一下最好提供一個接口頭文件
                   動態(tài)加載,用dlopen,dlclose,dlsym
             
            ref:http://niefei.blog.ccidnet.com/blog/ccid/do_showone/tid_42855.html
            1. 介紹
              使用GNU的工具我們?nèi)绾卧贚inux下創(chuàng)建自己的程序函數(shù)庫?一個“程序 函數(shù)庫”簡單的說就是一個文件包含了一些編譯好的代碼和數(shù)據(jù),這些編 譯好的代碼和數(shù)據(jù)可以在事后供其他的程序使用。程序函數(shù)庫可以使整個程序更加模塊化,更容易重新編譯,而且更方便升級。程序函數(shù)庫可分為3種類型:靜態(tài)函 數(shù)庫(static libraries)、共享函數(shù)庫(shared libraries)和動態(tài)加載函數(shù)庫(dynamically loaded libraries)。
              靜態(tài)函數(shù)庫是在程序執(zhí)行前就加入到目標(biāo)程序中去了;而共享函數(shù)庫則是在程序啟動的時候加載到程序中,它可以被 不同的程序共享;動態(tài)加載函數(shù)庫則可以在程序運行的任何時候動態(tài)的加載。實際上,動態(tài)函數(shù)庫并非另外一種庫函數(shù)格式,區(qū)別是動態(tài)加載函數(shù)庫是如何被程序員 使用的。后面我們將舉例說明。
              本文檔主要參考Program Library HOWTO,作者是luster(hwang@ustc.edu),任何非商業(yè)目的的再次發(fā)行本文檔都是允許的,但是請保留作者信息和本版權(quán)聲明。本文檔首先在www.linuxaid.com.cn發(fā)布。
              2. 靜態(tài)函數(shù)庫
               靜態(tài)函數(shù)庫實際上就是簡單的一個普通的目標(biāo)文件的集合,一般來說習(xí)慣用“.a”作為文件的后綴。可以用ar這個程序來產(chǎn)生靜態(tài)函數(shù)庫文件。Ar 是archiver的縮寫。靜態(tài)函數(shù)庫現(xiàn)在已經(jīng)不在像以前用得那么多了,主要是共享函數(shù)庫與之相比較有很多的優(yōu)勢的原因。慢慢地,大家都喜歡使用共享函數(shù) 庫了。不過,在一些場所靜態(tài)函數(shù)庫仍然在使用,一來是保持一些與以前某些程序的兼容,二來它描述起來也比較簡單。
              靜態(tài)庫函數(shù)允許程序 員把程序link起來而不用重新編譯代碼,節(jié)省了重新編譯代碼的時間。不過,在今天這么快速的計算機面前,一般的程序的重新編譯也花費不了多少時間,所以 這個優(yōu)勢已經(jīng)不是像它以前那么明顯了。靜態(tài)函數(shù)庫對開發(fā)者來說還是很有用的,例如你想把自己提供的函數(shù)給別人使用,但是又想對函數(shù)的源代碼進行保密,你就 可以給別人提供一個靜態(tài)函數(shù)庫文件。理論上說,使用ELF格式的靜態(tài)庫函數(shù)生成的代碼可以比使用共享函數(shù)庫(或者動態(tài)函數(shù) 庫)的程序運行速度上快一些,大概1-5%。
              創(chuàng)建一個靜態(tài)函數(shù)庫文件,或者往一個已經(jīng)存在地靜態(tài)函數(shù)庫文件添加新的目標(biāo)代碼,可以用下面的命令:
            ar rcs my_library.a file1.o file2.o
               這個例子中是把目標(biāo)代碼file1.o和file2.o加入到my_library.a這個函數(shù)庫文件中,如果my_library.a不存在 則創(chuàng)建一個新的文件。在用ar命令創(chuàng)建靜態(tài)庫函數(shù)的時候,還有其他一些可以選擇的參數(shù),可以參加ar的使用幫助。這里不再贅述。
              一旦 你創(chuàng)建了一個靜態(tài)函數(shù)庫,你可以使用它了。你可以把它作為你編譯和連接過程中的一部分用來生成你的可執(zhí)行代碼。如果你用gcc來編譯產(chǎn)生可 執(zhí)行代碼的話,你可以用“-l”參數(shù)來指定這個庫函數(shù)。你也可以用ld來做,使用它的“-l”和“-L”參數(shù)選項。具體用法,可以參考info:gcc。
             3. 共享函數(shù)庫
              共享函數(shù)庫中的函數(shù)是在當(dāng)一個可執(zhí)行程序在啟動的時候被加載。如果一個共享函數(shù)庫正常安裝,所有的程序在重新運行的時候都可以自動加載最新的函數(shù)庫中的函數(shù)。對于Linux系統(tǒng)還有更多的可以實現(xiàn)的功能:
            o 升級了函數(shù)庫但是仍然允許程序使用老版本的函數(shù)庫。 o 當(dāng)執(zhí)行某個特定程序的時候可以覆蓋某個特定的庫或者庫中指定的函數(shù)。 o 可以在庫函數(shù)被使用的過程中修改這些函數(shù)庫。
              3.1. 一些約定
               如果你要編寫的共享函數(shù)庫支持所有有用的特性,你在編寫的過程中必須遵循一系列約定。你必須理解庫的不同的名字間的區(qū)別,例如它的 “soname”和“real name”之間的區(qū)別和它們是如何相互作用的。你同樣還要知道你應(yīng)該把這些庫函數(shù)放在你文件系統(tǒng)的什么位置等等。下面我們具體看看這些問題。
              3.1.1. 共享庫的命名
              每個共享函數(shù)庫都有個特殊的名字,稱作“soname”。Soname名字命名必須以“lib”作為前綴,然后是函數(shù)庫的名字,然后是“.so”,最后是版本號信息。不過有個特例,就是非常底層的C庫函數(shù)都不是以lib開頭這樣命名的。
              每個共享函數(shù)庫都有一個真正的名字(“real name”),它是包含真正庫函數(shù)代碼的文件。真名有一個主版本號,和一個發(fā)行版本號。最后一個發(fā)行版本號是可選的,可以沒有。主版本號和發(fā)行版本號使你可以知道你到底是安裝了什么版本的庫函數(shù)。
            另外,還有一個名字是編譯器編譯的時候需要的函數(shù)庫的名字,這個名字就是簡單的soname名字,而不包含任何版本號信息。
               管理共享函數(shù)庫的關(guān)鍵是區(qū)分好這些名字。當(dāng)可執(zhí)行程序需要在自己的程序中列出這些他們需要的共享庫函數(shù)的時候,它只要用soname就可以了; 反過來,當(dāng)你要創(chuàng)建一個新的共享函數(shù)庫的時候,你要指定一個特定的文件名,其中包含很細節(jié)的版本信息。當(dāng)你安裝一個新版本的函數(shù)庫的時候,你只要先將這些 函數(shù)庫文件拷貝到一些特定的目錄中,運行l(wèi)dconfig這個實用就可以。Ldconfig檢查已經(jīng)存在的庫文件,然后創(chuàng)建soname的符號鏈接到真正 的函數(shù)庫,同時設(shè)置/etc/ld.so.cache這個緩沖文件。這個我們稍后再討論。
              Ldconfig并不設(shè)置鏈接的名字,通常 的做法是在安裝過程中完成這個鏈接名字的建立,一般來說這個符號鏈接就簡單的指向最新的soname 或者最新版本的函數(shù)庫文件。最好把這個符號鏈接指向soname,因為通常當(dāng)你升級你的庫函數(shù)的后,你就可以自動使用新版本的函數(shù)庫勒。
              我們來舉例看看:
               /usr/lib/libreadline.so.3 是一個完全的完整的soname,ldconfig可以設(shè)置一個符號鏈接到其他某個真正的函數(shù)庫文件,例如是 /usr/lib/libreadline.so.3.0。同時還必須有一個鏈接名字,例如/usr/lib/libreadline.so 就是一個符號鏈接指向/usr/lib/libreadline.so.3。
            3.1.2. 文件系統(tǒng)中函數(shù)庫文件的位置
               共享函數(shù)庫文件必須放在一些特定的目錄里,這樣通過系統(tǒng)的環(huán)境變量設(shè)置,應(yīng)用程序才能正確的使用這些函數(shù)庫。大部分的源碼開發(fā)的程序都遵循 GNU的一些標(biāo)準(zhǔn),我們可以看info幫助文件獲得相信的說明,info信息的位置是:info: standards#Directory_Variables。GNU標(biāo)準(zhǔn)建議所有的函數(shù)庫文件都放在/usr/local/lib目錄下,而且建議命令 可執(zhí)行程序都放在/usr/local/bin目錄下。這都是一些習(xí)慣問題,可以改變的。
              文件系統(tǒng)層次化標(biāo)準(zhǔn)FHS(Filesystem Hierarchy Standard)(http://www.pathname.com/fhs)規(guī)定了在一個發(fā)行包中大部分的函數(shù)庫文件應(yīng)該安裝到/usr/lib目錄 下,但是如果某些庫是在系統(tǒng)啟動的時候要加載的,則放到/lib目錄下,而那些不是系統(tǒng)本身一部分的庫則放到/usr/local/lib下面。
              上面兩個路徑的不同并沒有本質(zhì)的沖突。GNU提出的標(biāo)準(zhǔn)主要對于開發(fā)者開發(fā)源碼的,而FHS的建議則是針對發(fā)行版本的路徑的。具體的位置信息可以看/etc/ld.so.conf里面的配置信息。
              3.2. 這些函數(shù)庫如何使用
               在基于GNU glibc的系統(tǒng)里,包括所有的linux系統(tǒng),啟動一個ELF格式的二進制可執(zhí)行文件會自動啟動和運行一個program loader。對于Linux系統(tǒng),這個loader的名字是/lib/ld-linux.so.X(X是版本號)。這個loader啟動后,反過來就會 load所有的其他本程序要使用的共享函數(shù)庫。
              到底在哪些目錄里查找共享函數(shù)庫呢?這些定義缺省的是放在 /etc/ld.so.conf文件里面,我們可以修改這個文件,加入我們自己的一些 特殊的路徑要求。大多數(shù)RedHat系列的發(fā)行包的/etc/ld.so.conf文件里面不包括/usr/local/lib這個目錄,如果沒有這個目 錄的話,我們可以修改/etc/ld.so.conf,自己手動加上這個條目。
              如果你想覆蓋某個庫中的一些函數(shù),用自己的函數(shù)替換它們,同時保留該庫中其他的函數(shù)的話,你可以在/etc/ld.so.preload中加入你想要替換的庫(.o結(jié)尾的文件),這些preloading的庫函數(shù)將有優(yōu)先加載的權(quán)利。
               當(dāng)程序啟動的時候搜索所有的目錄顯然會效率很低,于是Linux系統(tǒng)實際上用的是一個高速緩沖的做法。Ldconfig缺省情況下讀出 /etc/ld.so.conf相關(guān)信息,然后設(shè)置適當(dāng)?shù)胤栨溄樱缓髮懸粋€cache到/etc/ld.so.cache這個文件中,而這個 /etc/ld.so.cache則可以被其他程序有效的使用了。這樣的做法可以大大提高訪問函數(shù)庫的速度。這就要求每次新增加一個動態(tài)加載的函數(shù)庫的時 候,就要運行l(wèi)dconfig來更新這個cache,如果要刪除某個函數(shù)庫,或者某個函數(shù)庫的路徑修改了,都要重新運行l(wèi)dconfig來更新這個 cache。通常的一些包管理器在安裝一個新的函數(shù)庫的時候就要運行l(wèi)dconfig。
              另外,F(xiàn)reeBSD使用cache的文件不一樣。FreeBSD的ELF cache是/var/run/ld-elf.so.hints,而a.out的cache責(zé)是/var/run/ld.so.hints。它們同樣是通過ldconfig來更新。
              3.3. 環(huán)境變量
               各種各樣的環(huán)境變量控制著一些關(guān)鍵的過程。例如你可以臨時為你特定的程序的一次執(zhí)行指定一個不同的函數(shù)庫。Linux系統(tǒng)中,通常變量 LD_LIBRARY_PATH就是可以用來指定函數(shù)庫查找路徑的,而且這個路徑通常是在查找標(biāo)準(zhǔn)的路徑之前查找。這個是很有用的,特別是在調(diào)試一個新的 函數(shù)庫的時候,或者在特殊的場合使用一個肥標(biāo)準(zhǔn)的函數(shù)庫的時候。環(huán)境變量LD_PRELOAD列出了所有共享函數(shù)庫中需要優(yōu)先加載的庫文件,功能和 /etc/ld.so.preload類似。這些都是有/lib/ld-linux.so這個loader來實現(xiàn)的。值得一提的是, LD_LIBRARY_PATH可以在大部分的UNIX-linke系統(tǒng)下正常起作用,但是并非所有的系統(tǒng)下都可以使用,例如HP-UX系統(tǒng)下,就是用 SHLIB_PATH這個變量,而在AIX下則使用LIBPATH這個變量。
              LD_LIBRARY_PATH在開發(fā)和調(diào)試過程中經(jīng)常大量使用,但是不應(yīng)該被一個普通用戶在安裝過程中被安裝程序修改,大家可以去參考 http://www.visi.com/~barr/ldpath.html,這里有一個文檔專門介紹為什么不使用LD_LIBRARY_PATH這個 變量。
              事實上還有更多的環(huán)境變量影響著程序的調(diào)入過程,它們的名字通常就是以LD_或者RTLD_打頭。大部分這些環(huán)境變量的使用的文檔都是不全,通常搞得人頭昏眼花的,如果要真正弄清楚它們的用法,最好去讀loader的源碼(也就是gcc的一部分)。
               允許用戶控制動態(tài)鏈接函數(shù)庫將涉及到setuid/setgid這個函數(shù)如果特殊的功能需要的話。因此,GNU loader通常限制或者忽略用戶對這些變量使用setuid和setgid。如果loader通過判斷程序的相關(guān)環(huán)境變量判斷程序的是否使用了 setuid或者setgid,如果uid和euid不同,或者gid和egid部一樣,那么loader就假定程序已經(jīng)使用了setuid或者 setgid,然后就大大的限制器控制這個老鏈接的權(quán)限。如果閱讀GNU glibc的庫函數(shù)源碼,就可以清楚地看到這一點,特別的我們可以看elf/rtld.c和sysdeps/generic/dl-sysdep.c這兩 個文件。這就意味著如果你使得uid和gid與euid和egid分別相等,然后調(diào)用一個程序,那么這些變量就可以完全起效。
            3.4. 創(chuàng)建一個共享函數(shù)庫
               現(xiàn)在我們開始學(xué)習(xí)如何創(chuàng)建一個共享函數(shù)庫。其實創(chuàng)建一個共享函數(shù)庫非常容易。首先創(chuàng)建object文件,這個文件將加入通過gcc –fPIC 參數(shù)命令加入到共享函數(shù)庫里面。PIC的意思是“位置無關(guān)代碼”(Position Independent Code)。下面是一個標(biāo)準(zhǔn)的格式:
            gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list
              下面再給一個例子,它創(chuàng)建兩個object文件(a.o和b.o),然后創(chuàng)建一個包含a.o和b.o的共享函數(shù)庫。例子中”-g”和“-Wall”參數(shù)不是必須的。
            gcc -fPIC -g -c -Wall a.cgcc -fPIC -g -c -Wall b.cgcc -shared -Wl,
            -soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o -lc
              下面是一些需要注意的地方:
            · 不用使用-fomit-frame-pointer這個編譯參數(shù)除非你不得不這樣。雖然使用了這個參數(shù)獲得的函數(shù)庫仍然可以使用,但是這使得調(diào)試程序幾乎 沒有用,無法跟蹤調(diào)試。 · 使用-fPIC來產(chǎn)生代碼,而不是-fpic。 · 某些情況下,使用gcc 來生成object文件,需要使用“-Wl,-export-dynamic”這個選項參數(shù)。通常,動態(tài)函數(shù)庫的符號表里面包含了這些動態(tài)的對象的符號。 這個選項在創(chuàng)建ELF格式的文件時候,會將所有的符號加入到動態(tài)符號表中。可以參考ld的幫助獲得更詳細的說明。
              3.5. 安裝和使用共享函數(shù)庫
              一旦你了一個共享函數(shù)庫,你還需要安裝它。其實簡單的方法就是拷貝你的庫文件到指定的標(biāo)準(zhǔn)的目錄(例如/usr/lib),然后運行l(wèi)dconfig。
               如果你沒有權(quán)限去做這件事情,例如你不能修改/usr/lib目錄,那么你就只好通過修改你的環(huán)境變量來實現(xiàn)這些函數(shù)庫的使用了。首先,你需要 創(chuàng)建這些共享函數(shù)庫;然后,設(shè)置一些必須得符號鏈接,特別是從soname到真正的函數(shù)庫文件的符號鏈接,簡單的方法就是運行l(wèi)dconfig:
            ldconfig -n directory_with_shared_libraries
              然后你就可以設(shè)置你的LD_LIBRARY_PATH這個環(huán)境變量,它是一個以逗號分隔的路徑的集合,這個可以用來指明共享函數(shù)庫的搜索路徑。例如,使用bash,就可以這樣來啟動一個程序my_program:
            LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH my_program
               如果你需要的是重載部分函數(shù),則你就需要創(chuàng)建一個包含需要重載的函數(shù)的object文件,然后設(shè)置LD_PRELOAD環(huán)境變量。通常你可以很 方便的升級你的函數(shù)庫,如果某個API改變了,創(chuàng)建庫的程序會改變soname。然而,如果一個函數(shù)升級了某個函數(shù)庫而保持了原來的soname,你可以 強行將老版本的函數(shù)庫拷貝到某個位置,然后重新命名這個文件(例如使用原來的名字,然后后面加.orig后綴),然后創(chuàng)建一個小的“wrapper”腳本 來設(shè)置這個庫函數(shù)和相關(guān)的東西。例如下面的例子:
            #!/bin/sh export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH exec
            /usr/bin/my_program.orig $*
              我們可以通過運行l(wèi)dd來看某個程序使用的共享函數(shù)庫。例如你可以看ls這個實用工具使用的函數(shù)庫:
            ldd /bin/ls
                libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001c000)
                libc.so.6 => /lib/libc.so.6 (0x40020000)
                /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
              通常我么可以看到一個soname的列表,包括路徑。在所有的情況下,你都至少可以看到兩個庫:
            · /lib/ld-linux.so.N(N是1或者更大,一般至少2)。
            這是這個用力加載其他所有的共享庫的庫。
            · libc.so.N(N應(yīng)該大于或者等于6)。這是C語言函數(shù)庫。
               值得一提的是,不要在對你不信任的程序運行l(wèi)dd命令。在ldd的manual里面寫得很清楚,ldd是通過設(shè)置某些特殊的環(huán)境變量(例如,對 于ELF對象,設(shè)置LD_TRACE_LOADED_OBJECTS),然后運行這個程序。這樣就有可能使得某地程序可能使得ldd來執(zhí)行某些意想不到的 代碼,而產(chǎn)生不安全的隱患。
            3.6. 不兼容的函數(shù)庫
              如果一個新版的函數(shù)庫要和老版本的二進制的庫不兼容,則soname需要改變。對于C語言,一共有4個基本的理由使得它們在二進制代碼上很難兼容:
              o. 一個函數(shù)的行文改變了,這樣它就可能與最開始的定義不相符合。
              o. 輸出的數(shù)據(jù)項改變了。
              o. 某些輸出的函數(shù)刪除了。
              o. 某些輸出函數(shù)的接口改變了。
              如果你能避免這些地方,你就可以保持你的函數(shù)庫在二進制代碼上的兼容,或者說,你可以使得你的程序的應(yīng)用二進制接口(ABI:Application Binary Interface)上兼容。
              4. 動態(tài)加載的函數(shù)庫Dynamically Loaded (DL) Libraries
               動態(tài)加載的函數(shù)庫Dynamically loaded (DL) libraries是一類函數(shù)庫,它可以在程序運行過程中的任何時間加載。它們特別適合在函數(shù)中加載一些模塊和plugin擴展模塊的場合,因為它可以在 當(dāng)程序需要某個plugin模塊時才動態(tài)的加載。例如,Pluggable Authentication Modules(PAM)系統(tǒng)就是用動態(tài)加載函數(shù)庫來使得管理員可以配置和重新配置身份驗證信息。
              Linux系統(tǒng)下,DL函數(shù)庫與其 他函數(shù)庫在格式上沒有特殊的區(qū)別,我們前面提到過,它們創(chuàng)建的時候是標(biāo)準(zhǔn)的object格式。主要的區(qū)別就是 這些函數(shù)庫不是在程序鏈接的時候或者啟動的時候加載,而是通過一個API來打開一個函數(shù)庫,尋找符號表,處理錯誤和關(guān)閉函數(shù)庫。通常C語言環(huán)境下,需要包 含這個頭文件。
              Linux中使用的函數(shù)和Solaris中一樣,都是dlpoen() API。當(dāng)時不是所有的平臺都使用同樣的接口,例如HP-UX使用shl_load()機制,而Windows平臺用另外的其他的調(diào)用接口。如果你的目的 是使得你的代碼有很強的移植性,你應(yīng)該使用一些wrapping函數(shù)庫,這樣的wrapping函數(shù)庫隱藏不同的平臺的接口區(qū)別。一種方法是使用 glibc函數(shù)庫中的對動態(tài)加載模塊的支持,它使用一些潛在的動態(tài)加載函數(shù)庫界面使得它們可以夸平臺使用。具體可以參考http: //developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html. 另外一個方法是使用libltdl,是GNU libtool的一部分,可以進一步參考CORBA相關(guān)資料。
              4.1. dlopen()
              dlopen函數(shù)打開一個函數(shù)庫然后為后面的使用做準(zhǔn)備。C語言原形是:
            void * dlopen(const char *filename, int flag);
              如果文件名filename是以“/”開頭,也就是使用絕對路徑,那么dlopne就直接使用它,而不去查找某些環(huán)境變量或者系統(tǒng)設(shè)置的函數(shù)庫所在的目錄了。否則dlopen()
              就會按照下面的次序查找函數(shù)庫文件:
            1. 環(huán)境變量LD_LIBRARY指明的路徑。 2. /etc/ld.so.cache中的函數(shù)庫列表。 3. /lib目錄,然后/usr/lib。不過一些很老的a.out的loader則是采用相反的次序,也就是先查/usr/lib,然后是/lib。
               Dlopen()函數(shù)中,參數(shù)flag的值必須是RTLD_LAZY或者RTLD_NOW,RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,而RTLD_NOW的含義是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'。
              如果有好幾個函數(shù)庫,它們之間有一些依賴關(guān)系的話,例如X依賴Y,那么你就要先加載那些被依賴的函數(shù)。例如先加載Y,然后加載X。
              dlopen()函數(shù)的返回值是一個句柄,然后后面的函數(shù)就通過使用這個句柄來做進一步的操作。如果打開失敗dlopen()就返回一個NULL。如果一個函數(shù)庫被多次打開,它會返回同樣的句柄。
              如果一個函數(shù)庫里面有一個輸出的函數(shù)名字為_init,那么_init就會在dlopen()這個函數(shù)返回前被執(zhí)行。我們可以利用這個函數(shù)在我的函數(shù)庫里面做一些初始化的工作。我們后面會繼續(xù)討論這個問題的。
              4.2. dlerror()
              通過調(diào)用dlerror()函數(shù),我們可以獲得最后一次調(diào)用dlopen(),dlsym(),或者dlclose()的錯誤信息。
            4.3. dlsym()
              如果你加載了一個DL函數(shù)庫而不去使用當(dāng)然是不可能的了,使用一個DL函數(shù)庫的最主要的一個函數(shù)就是dlsym(),這個函數(shù)在一個已經(jīng)打開的函數(shù)庫里面查找給定的符號。這個函數(shù)如下定義:
            void * dlsym(void *handle, char *symbol);
              函數(shù)中的參數(shù)handle就是由dlopen打開后返回的句柄,symbol是一個以NIL結(jié)尾的字符串。
               如果dlsym()函數(shù)沒有找到需要查找的symbol,則返回NULL。如果你知道某個symbol的值不可能是NULL或者0,那么就很 好,你就可以根據(jù)這個返回結(jié)果判斷查找的symbol是否存在了;不過,如果某個symbol的值就是NULL,那么這個判斷就有問題了。標(biāo)準(zhǔn)的判斷方法 是先調(diào)用dlerror(),清除以前可能存在的錯誤,然后調(diào)用dlsym()來訪問一個symbol,然后再調(diào)用dlerror()來判斷是否出現(xiàn)了錯 誤。一個典型的過程如下:
            dlerror();
            s = (actual_type) dlsym(handle, symbol_being_searched_for);
            if ((err = dlerror()) != NULL)
            {
            }
            else
            {
            }
              4.4. dlclose()
               dlopen()函數(shù)的反過程就是dlclose()函數(shù),dlclose()函數(shù)用力關(guān)閉一個DL函數(shù)庫。Dl函數(shù)庫維持一個資源利用的計數(shù) 器,當(dāng)調(diào)用dlclose的時候,就把這個計數(shù)器的計數(shù)減一,如果計數(shù)器為0,則真正的釋放掉。真正釋放的時候,如果函數(shù)庫里面有_fini()這個函 數(shù),則自動調(diào)用_fini()這個函數(shù),做一些必要的處理。Dlclose()返回0表示成功,其他非0值表示錯誤。
              4.5. DL Library Example
              下面是一個例子。例子中調(diào)入math函數(shù)庫,然后打印2.0的余弦函數(shù)值。例子中每次都檢查是否出錯。應(yīng)該是個不錯的范例:
              #include
              #include
              #include
              int main(int argc, char **argv)
              {
                void *handle;
                double (*cosine)(double);
                char *error;
                handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
                if (!handle) {
                    fputs (dlerror(), stderr);
                    exit(1);
                }
                cosine = dlsym(handle, "cos");
                if ((error = dlerror()) != NULL)
              {
                    fputs(error, stderr);
                    exit(1);
                }
                printf ("%f ", (*cosine)(2.0));
                dlclose(handle);
            }
              如果這個程序名字叫foo.c,那么用下面的命令來編譯:
              gcc -o foo foo.c -ldl
            5. 其他
              5.1. nm命令
               nm命令可以列出一個函數(shù)庫文件中的符號表。它對于靜態(tài)的函數(shù)庫和共享的函數(shù)庫都起作用。對于一個給定的函數(shù)庫,nm命令可以列出函數(shù)庫中定義 的所有符號,包括每個符號的值和類型。還可以給出在原程序中這個函數(shù)(符號)是在多少行定義的,不過這必須要求編譯該函數(shù)庫的時候加“-l”選項。
               關(guān)于符號的類型,這里我們再多討論一下。符號的類型是以一個字母的形式顯示的,小寫字母表示這個符號是本地(local)的,而大寫字母則表示 這個符號是全局的(global,externel)。一般來說,類型有一下幾種:T、D、B、U、W。各自的含義如下:T表示在代碼段中定義的一般變量 符號;D表示時初始化過的數(shù)據(jù)段;B表示初始化的數(shù)據(jù)段;U表示沒有定義的,在這個庫里面使用了,但是在其他庫中定義的符號;W,weak的縮寫,表示如 果其他函數(shù)庫中也有對這個符號的定義,則其他符號的定義可以覆蓋這個定義。
              如果你知道一個函數(shù)的名字,但是你不知道這個函數(shù)在什么庫中定義的,那么可以用mn的“-o”選項和grep命令來查找?guī)斓拿帧?o選項使得顯示的每一行都有這個函數(shù)庫文件名。例如,你要查找“cos”這個是在什么地方定義的,大致可以用下面的命令:
            nm -o /lib* /usr/local/libGROUP ( /lib/libc.so.6
            /usr/lib/libc_nonshared.a )
              更多的信息可以參考texinfo文檔中關(guān)于ld鏈接的腳本部分。一般的信息還可以參考: info:ld#Options 和info:ld#Commands,也可以參考info:ld#Option Commands。
              5.4. GNU libtool
               如果你正在編譯的系統(tǒng)相很方便的移植到其他操作系統(tǒng)下,你可以使用GNU libtool來創(chuàng)建和安裝這個函數(shù)庫。GNU libtool是一個函數(shù)庫支持的典型的腳本。Libtool隱藏了使用一個可移植的函數(shù)庫的負責(zé)性。Libtool提供了一個可以移植的界面來創(chuàng)建 object文件,鏈接函數(shù)庫(靜態(tài)或者共享的),并且安裝這些庫。它還包含了libltdl,一個可移植的動態(tài)函數(shù)庫調(diào)入程序的wrapper。更多的 詳細討論,可以在http://www.gnu.org/software/libtool/manual.html看到。
              5.5. 刪除一些符號
              在一個生產(chǎn)的文件中很多符號都是為了debug而包含的,占用了不少空間。如果空間不夠,而且這些符號也許不再需要,就可以將其中一些刪除。
               最好的方法就是先正常的生成你需要的object文件,然后debug和測試你需要的一些東西。一旦你完全測試完畢了,就可以用strip去刪 除一些不需要的符號了。Strip命令可以使你很方便的控制刪除什么符號,而保留什么符號。Strip的具體用法可以參考其幫助文件。
              另外的方法就是使用GNU ld的選項“-S”和“-s”;“-S”會刪除一些debugger的符號,而“-s”則是將所有的符號信息都刪除。通常我們可以在gcc中加這樣的參數(shù)“-Wl,-S”和“-Wl,-s”來達到這個目的。
            摘要
            下 面是一些例子,例子中我們會使用三種函數(shù)庫(靜態(tài)的、共享的和動態(tài)加載的函數(shù)庫)。文件libhello.c是一個函數(shù)庫,libhello.h 是它的頭文件;demo_use.c則是一個使用了libhello函數(shù)庫的。Script_static和script_dynamic分別演示如何以 靜態(tài)和共享方式使用函數(shù)庫,而后面的demo_dynamic.c和script_dynamic則表示演示如何以動態(tài)加載函數(shù)庫的方式來使用它。
            (2002-08-25 17:38:37)
            By Wing
              6. 更多的例子
               下面是一些例子,例子中我們會使用三種函數(shù)庫(靜態(tài)的、共享的和動態(tài)加載的函數(shù)庫)。文件libhello.c是一個函數(shù)庫, libhello.h是它的頭文件;demo_use.c則是一個使用了libhello函數(shù)庫的。Script_static和 script_dynamic分別演示如何以靜態(tài)和共享方式使用函數(shù)庫,而后面的demo_dynamic.c和script_dynamic則表示演示 如何以動態(tài)加載函數(shù)庫的方式來使用它。
              6.1. File libhello.c
            #include
            void hello(void)
            {
            printf("Hello, library world.
            ");
            }
              6.2. File libhello.h
            void hello(void);
              6.3. File demo_use.c
            #include "libhello.h"
            int main(void)
            {
            hello();
            return 0;
            }
              6.4. File script_static
            #!/bin/sh
            # Static library demo
            # Create static library's object file, libhello-static.o.
            # I'm using the name libhello-static to clearly
            # differentiate the static library from the
            # dynamic library examples, but you don't need to use
            # "-static" in the names of your
            # object files or static libraries.gcc -Wall -g -c -o libhello-static.o
            libhello.c
            # Create static library.ar rcs libhello-static.a libhello-static.o
            # At this point we could just copy libhello-static.a
            # somewhere else to use it.
            # For demo purposes, we'll just keep the library
            # in the current directory.
            # Compile demo_use program file.gcc -Wall -g -c demo_use.c -o demo_use.o
            # Create demo_use program; -L. causes "." to be searched during
            # creation of the program. Note that this command causes
            # the relevant object file in libhello-static.a to be
            # incorporated into file demo_use_static.gcc -g -o demo_use_static
            demo_use.o -L. -lhello-static
            # Execute the program../demo_use_static
              6.5. File script_shared
            #!/bin/sh
            # Shared library demo
            # Create shared library's object file, libhello.o.gcc -fPIC -Wall
            -g -c libhello.c
            # Create shared library.
            # Use -lc to link it against C library, since libhello
            # depends on the C library.gcc -g -shared -Wl,-soname,libhello.so.0 -o
            libhello.so.0.0 libhello.o -lc# At this point we could just copy
            libhello.so.0.0 into
            # some directory, say /usr/local/lib.
            # Now we need to call ldconfig to fix up the symbolic links.
            # Set up the soname. We could just execute:
            # ln -sf libhello.so.0.0 libhello.so.0
            # but let's let ldconfig figure it out./sbin/ldconfig -n .
            # Set up the linker name.
            # In a more sophisticated setting, we'd need to make
            # sure that if there was an existing linker name,
            # and if so, check if it should stay or not.ln -sf libhello.so.0
            libhello.so
            # Compile demo_use program file.gcc -Wall -g -c demo_use.c -o
            demo_use.o
            # Create program demo_use.
            # The -L. causes "." to be searched during creation
            # of the program; note that this does NOT mean that "."
            # will be searched when the program is executed.gcc -g -o demo_use
            demo_use.o -L. -lhello
            # Execute the program. Note that we need to tell the program
            # where the shared library is,
            using LD_LIBRARY_PATH.LD_LIBRARY_PATH="." ./demo_use
              6.6. File demo_dynamic.c
            #include
            #include
            #include
            typedef void (*simple_demo_function)(void);
            int main(void)
            {
            const char *error;
            void *module;
            simple_demo_function demo_function;
            module = dlopen("libhello.so", RTLD_LAZY);
            if (!module)
            {
              fprintf(stderr, "Couldn't open libhello.so: %s
            ",dlerror());
              exit(1);
            }
            dlerror();
            demo_function = dlsym(module, "hello");
            if ((error = dlerror()))
            {
              fprintf(stderr, "Couldn't find hello: %s
            ", error);
              exit(1);
            }
            (*demo_function)();
            dlclose(module);
            return 0;
            }
              6.7. File script_dynamic
            #!/bin/sh
            # Dynamically loaded library demo
            # Presume that libhello.so and friends have
            # been created (see dynamic example).
            # Compile demo_dynamic program file into an object file.gcc
            -Wall -g -c demo_dynamic.c
            # Create program demo_use.
            # Note that we don't have to tell it where to search
            for DL libraries,
            # since the only special library this program uses won't be
            # loaded until after the program starts up.
            # However, we DO need the option -ldl to include the library
            # that loads the DL libraries.gcc -g -o demo_dynamic
            demo_dynamic.o -ldl
            # Execute the program. Note that we need to tell the
            # program where get the dynamically loaded library,
            # using LD_LIBRARY_PATH.LD_LIBRARY_PATH="." ./demo_dynamic

            posted @ 2012-12-07 22:34 tqsheng 閱讀(246) | 評論 (0)編輯 收藏

            linux下動態(tài)庫so文件的一些認(rèn)識

            個人創(chuàng)作,歡迎指錯。 
            牽扯到ELF格式,gcc編譯選項待補,簡單實用的說明一下,對Linux下的so文件有個實際性的認(rèn)識。 
            1.so文件是什么? 
            2.怎么生成以及使用一個so動態(tài)庫文件? 
            3.地址空間,以及線程安全. 
            4.庫的初始化,解析: 
            5.使用我們自己庫里的函數(shù)替換系統(tǒng)函數(shù): 
            //------------------------------------------------------------------------------- 
            1.so文件是什么? 
            也是ELF格式文件,共享庫(動態(tài)庫),類似于DLL。節(jié)約資源,加快速度,代碼升級簡化。 
            知道這么多就夠了,實用主義。等有了印象再研究原理。 
            2.怎么生成以及使用一個so動態(tài)庫文件? 
            先寫一個C文件:s.c 
            C代碼  
            #include <stdio.h>  
            int count;  
            void out_msg(const char *m)  
            {//2秒鐘輸出1次信息,并計數(shù)  
             for(;;) {printf("%s %d\n", m, ++count); sleep(2);}  
            }  
            編譯:得到輸出文件libs.o 
            gcc -fPIC -g -c s.c -o libs.o 
              
            鏈接:得到輸出文件libs.so 
            gcc -g -shared -Wl,-soname,libs.so -o libs.so libs.o -lc 
            一個頭文件:s.h 
            C代碼  
            #ifndef _MY_SO_HEADER_  
            #define _MY_SO_HEADER_  
            void out_msg(const char *m);  
            #endif  
            再來一個C文件來引用這個庫中的函數(shù):ts.c 
            C代碼  
            #include <stdio.h>  
             #include "s.h"  
             int main(int argc, char** argv)  
             {  
              printf("TS Main\n");  
              out_msg("TS ");  
              sleep(5);  //這句話可以注釋掉,在第4節(jié)的時候打開就可以。  
              printf("TS Quit\n");  
             }  
            編譯鏈接這個文件:得到輸出文件ts 
            gcc -g ts.c -o ts -L. -ls 
            執(zhí)行./ts,嗯:成功了。。。還差點 
            得到了ts:error while loading shared libraries: libs.so: cannot open shared object file: No such file or directory 
            系統(tǒng)不能找到我們自己定義的libs.so,那么告訴他,修改變量LD_LIBRARY_PATH,為了方便,寫個腳本:e(文件名就叫e,懶得弄長了) 
            #!/bin/sh 
            export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH} 
            ./ts 
            執(zhí)行:./e & 
            屏幕上就開始不停有信息輸出了,當(dāng)然TS Quit你是看不到的,前面是個死循環(huán),后面會用到這句 
            3.地址空間,以及線程安全: 
            如果這樣: 
            ./e &開始執(zhí)行后,稍微等待一下然后再 ./e&, 
            這個時候屏幕信息會怎么樣呢?全局變量count會怎么變化? 
            會是兩個進程交叉輸出信息,并且各自的count互不干擾,雖然他們引用了同一個so文件。 
            也就是說只有代碼是否線程安全一說,沒有代碼是否是進程安全這一說法。 
            4.庫的初始化,解析: 
            windows下的動態(tài)庫加載,卸載都會有初始化函數(shù)以及卸載函數(shù)來完成庫的初始化以及資源回收,linux當(dāng)然也可以實現(xiàn)。 
            ELF文件本身執(zhí)行時就會執(zhí)行一個_init()函數(shù)以及_fini()函數(shù)來完成這個,我們只要把自己的函數(shù)能讓系統(tǒng)在這個時候執(zhí)行 
            就可以了。 
            修改我們前面的s.c文件: 
            C代碼  
            #include <stdio.h>  
             void my_init(void) __attribute__((constructor)); //告訴gcc把這個函數(shù)扔到init section  
             void my_fini(void) __attribute__((destructor));  //告訴gcc把這個函數(shù)扔到fini section  
             void out_msg(const char *m)  
             {  
              printf(" Ok!\n");   
             }  
             int i; //仍然是個計數(shù)器  
             void my_init(void)  
             {  
              printf("Init ... ... %d\n", ++i);  
             }  
             void my_fini(void)  
             {  
              printf("Fini ... ... %d\n", ++i);  
             }  
            重新制作 libs.so,ts本是不用重新編譯了,代碼維護升級方便很多。 
            然后執(zhí)行: ./e & 
            可以看到屏幕輸出:(不完整信息,只是順序一樣) 
            Init 
            Main 
            OK 
            Quit 
            Fini 
            可以看到我們自己定義的初始化函數(shù)以及解析函數(shù)都被執(zhí)行了,而且是在最前面以及最后面。 
            如果s.c中的sleep(5)沒有注釋掉,那么有機會: 
            ./e& 
            ./e&連續(xù)執(zhí)行兩次,那么初始化函數(shù)和解析函數(shù)也會執(zhí)行兩次,雖然系統(tǒng)只加載了一次libs.so。 
            如果sleep時候kill 掉后臺進程,那么解析函數(shù)不會被執(zhí)行。 
            5.使用我們自己庫里的函數(shù)替換系統(tǒng)函數(shù): 
            創(chuàng)建一個新的文件b.c:我們要替換系統(tǒng)函數(shù)malloc以及free(可以自己寫個內(nèi)存泄露檢測工具了) 
            C代碼  
            #include <stdio.h>  
             void* malloc(int size)  
             {  
              printf("My malloc\n");  
              return NULL;  
             }  
             void free(void* ad)  
             {  
              printf("My free\n");  
             }  
            老規(guī)矩,編譯鏈接成一個so文件:得到libb.so 
            gcc -fPIC -g -c b.c -o libb.o 
            gcc -g -shared -Wl,-soname,libb.so -o libb.so -lc 
            修改s.c:重新生成libs.so 
            C代碼  
            void out_msg()  
             {  
              int *p;  
              p = (int*)malloc(100);  
              free(p);  
              printf("Stop Ok!\n");  
             }  
            修改腳本文件e: 
            #!/bin/sh 
            export LD_PRELOAD=${pwd}libb.so:${LD_PRELOAD} 
            export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH} 
            ./ts 
            關(guān)鍵就在LD_PRELOAD上了,這個路徑指定的so將在所有的so之前加載,并且符號會覆蓋后面加載的so文件中的符號。如果可執(zhí)行文件的權(quán)限不合適(SID),這個變量會被忽略。 
            執(zhí)行:./e & 
            嗯,可以看到我們的malloc,free工作了。 
            暫時就想到這么多了。

            posted @ 2012-12-07 22:33 tqsheng 閱讀(726) | 評論 (0)編輯 收藏

            Linux下動態(tài)庫只導(dǎo)出部分函數(shù)

            編譯一個1.c文件:
            #include "stdio.h"
            #if defined(__GNUC__) && \
                    ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
            #define NP_VISIBILITY_DEFAULT __attribute__((visibility("default")))
            #else
            #define NP_VISIBILITY_DEFAULT
            #endif
            #define NP_EXPORT(__type) NP_VISIBILITY_DEFAULT __type
            NP_EXPORT(int) a()
            {
            printf("1.c i am a\0");
            return 0;
            }
            int b()
            {
            return 0;
            }
            我的目的是默認(rèn)沒有使用NP_EXPORT宏的函數(shù)都隱藏起來,即把b函數(shù)給隱藏起來,而a函數(shù)則導(dǎo)出去。
            我的編譯步驟和指令如下:
            gcc -c 1.c
            gcc –fPIC -shared -fvisibility=hidden  -o 1.so 1.o
            編譯后生成了1.so文件。
            我使用nm工具查看,這個時候就可以達到隱藏不必要的函數(shù),而只是導(dǎo)出定義過的函數(shù)。

            posted @ 2012-12-07 22:28 tqsheng 閱讀(1310) | 評論 (0)編輯 收藏

            Linux中.a,.la,.o,.so文件的意義和編程實現(xiàn)_動態(tài)鏈接

            Linux下文件的類型是不依賴于其后綴名的,但一般來講:
              .o,是目標(biāo)文件,相當(dāng)于windows中的.obj文件
              .so 為共享庫,是shared object,用于動態(tài)連接的,和dll差不多
              .a為靜態(tài)庫,是好多個.o合在一起,用于靜態(tài)連接
              .la為libtool自動生成的一些共享庫,vi編輯查看,主要記錄了一些配置信息。可以用如下命令查看.la文件的格式 $file .la
              .la: ASCII English text
              所以可以用vi來查看其內(nèi)容。
              創(chuàng)建.a庫文件和.o庫文件:
              [yufei@localhost perl_c2]$ pwd
              /home/yufei/perl_c2
              [yufei@localhost perl_c2]$ cat mylib.c
              #include 
              #include 
              void hello(){
              printf("success call from perl to c library\n");
              }
              [yufei@localhost perl_c2]$ cat mylib.h
              extern void hello();
              [yufei@localhost perl_c2]$ gcc -c mylib.c
              [yufei@localhost perl_c2]$ dir
              mylib.c mylib.h mylib.o
              [yufei@localhost perl_c2]$ ar -r mylib.a mylib.o
              ar: 正在創(chuàng)建 mylib.a
              [yufei@localhost perl_c2]$ dir
              mylib.a mylib.c mylib.h mylib.o
              動態(tài)鏈接庫.so的編譯與使用- -
              動態(tài)庫.so在Linux下用c和c++編程時經(jīng)常會碰到,最近在網(wǎng)站找了幾篇文章介紹動態(tài)庫的編譯和鏈接,總算搞懂了這個之前一直不太了解得東東,這里做個筆記,也為其它正為動態(tài)庫鏈接庫而苦惱的兄弟們提供一點幫助。
              1、動態(tài)庫的編譯
              下面通過一個例子來介紹如何生成一個動態(tài)庫。這里有一個頭文件:so_test.h,三個.c文件:test_a.c、test_b.c、test_c.c,我們將這幾個文件編譯成一個動態(tài)庫:libtest.so。
              so_test.h:
              #include 
              #include 
              void test_a();
              void test_b();
              void test_c();
              test_a.c:
              #include "so_test.h"
              void test_a()
              {
              printf("this is in test_a...\n");
              }
              test_b.c:
              #include "so_test.h"
              void test_b()
              {
              printf("this is in test_b...\n");
              }
              test_c.c:
              #include "so_test.h"
              void test_c()
              {
              printf("this is in test_c...\n");
              }
              將這幾個文件編譯成一個動態(tài)庫:libtest.so
              $ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so 來
            2、動態(tài)庫的鏈接
              在1、中,我們已經(jīng)成功生成了一個自己的動態(tài)鏈接庫libtest.so,下面我們通過一個程序來調(diào)用這個庫里的函數(shù)。程序的源文件為:test.c。
              test.c:
              #include "so_test.h"
              int main()
              {
              test_a();
              test_b();
              test_c();
              return 0;
              }
              l 將test.c與動態(tài)庫libtest.so鏈接生成執(zhí)行文件test:
              $ gcc test.c -L. -ltest -o test
              l 測試是否動態(tài)連接,如果列出libtest.so,那么應(yīng)該是連接正常了
              $ ldd test
              l 執(zhí)行test,可以看到它是如何調(diào)用動態(tài)庫中的函數(shù)的。
              3、編譯參數(shù)解析
              最主要的是GCC命令行的一個選項:
              -shared 該選項指定生成動態(tài)連接庫(讓連接器生成T類型的導(dǎo)出符號表,有時候也生成弱連接W類型的導(dǎo)出符號),不用該標(biāo)志外部程序無法連接。相當(dāng)于一個可執(zhí)行文件
              -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關(guān)的所以動態(tài)載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。
              -L.:表示要連接的庫在當(dāng)前目錄中
              -ltest:編譯器查找動態(tài)連接庫時有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱
              LD_LIBRARY_PATH:這個環(huán)境變量指示動態(tài)連接器可以裝載動態(tài)庫的路徑。
              當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root權(quán)限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
              4、注意
              調(diào)用動態(tài)庫的時候有幾個問題會經(jīng)常碰到,有時,明明已經(jīng)將庫的頭文件所在目錄 通過 “-I” include進來了,庫所在文件通過“-L”參數(shù)引導(dǎo),并指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定鏈接的so文件,這時你要作的就是通過修改LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動態(tài)庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。
              makefile里面怎么正確的編譯和連接生成.so庫文件,然后又是在其他程序的makefile里面如何編譯和連接才能調(diào)用這個庫文件的函數(shù)????
              答:
              你需要告訴動態(tài)鏈接器、加載器ld.so在哪里才能找到這個共享庫,可以設(shè)置環(huán)境變量把庫的路徑添加到庫目錄/lib和/usr/lib,LD_LIBRARY_PATH=$(pwd),這種方法采用命令行方法不太方便,一種替代方法
              注釋
              LD_LIBRARY_PATH可以在/etc/profile還是 ~/.profile還是 ./bash_profile里設(shè)置,或者.bashrc里,
              改完后運行source /etc/profile或 . /etc/profile
              更好的辦法是添入/etc/ld.so.conf, 然后執(zhí)行 /sbin/ldconfig
              注釋
              是把庫路徑添加到/etc/ld.so.conf,然后以root身份運行l(wèi)dconfig
              也可以在連接的時候指定文件路徑和名稱 -I -L.
              GCC=gcc
              CFLAGS=-Wall -ggdb -fPIC
              #CFLAGS=
              all: libfunc test
              libfunc:func.o func1.o
              $(GCC) -shared -Wl,-soname,libfunc.so.1 -o libfunc.so.1.1 $<
              ln -sf libfunc.so.1.1 libfunc.so.1
              ln -sf libfunc.so.1 libfunc.so
              注釋
              ln -s是用來創(chuàng)建軟鏈接,也就相當(dāng)于windows中的快捷方式,在當(dāng)前目錄中創(chuàng)建上一級目錄中的文件ttt的命名為ttt2軟鏈接的命令是ln -s ../ttt ttt2,如果原文件也就是ttt文件刪除的話,ttt2也變成了空文件。
              ln -d是用來創(chuàng)建硬鏈接,也就相當(dāng)于windows中文件的副本,當(dāng)原文件刪除的時候,并不影響“副本”的內(nèi)容。
              編譯目標(biāo)文件時使用gcc的-fPIC選項,產(chǎn)生與位置無關(guān)的代碼并能被加載到任何地址:
              gcc –fPIC –g –c liberr.c –o liberr.o
              使用gcc的-shared和-soname選項;
              使用gcc的-Wl選項把參數(shù)傳遞給連接器ld;
              使用gcc的-l選項顯示的連接C庫,以保證可以得到所需的啟動(startup)代碼,從而避免程序在使用不同的,可能不兼容版本的C庫的系統(tǒng)上不能啟動執(zhí)行。
              gcc –g –shared –Wl,-soname,liberr.so –o liberr.so.1.0.0 liberr.o –lc
              建立相應(yīng)的符號連接:
              ln –s liberr.so.1.0.0 liberr.so.1;
              ln –s liberr.so.1.0.0 liberr.so;
              在MAKEFILE中:
              $@
              表示規(guī)則中的目標(biāo)文件集。在模式規(guī)則中,如果有多個目標(biāo),那么,"$@"就是匹配于目標(biāo)中模式定義的集合。
              $%
              僅當(dāng)目標(biāo)是函數(shù)庫文件中,表示規(guī)則中的目標(biāo)成員名。例如,如果一個目標(biāo)是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目標(biāo)不是函數(shù)庫文件(Unix下是[.a],Windows下是[.lib]),那么,其值為空。
              $<
              依賴目標(biāo)中的第一個目標(biāo)名字。如果依賴目標(biāo)是以模式(即"%")定義的,那么"$<"將是符合模式的一系列的文件集。注意,其是一個一個取出來的。
              $?
              所有比目標(biāo)新的依賴目標(biāo)的集合。以空格分隔。
              $
              所有的依賴目標(biāo)的集合。以空格分隔。如果在依賴目標(biāo)中有多個重復(fù)的,那個這個變量會去除重復(fù)的依賴目標(biāo),只保留一份。
              注釋
              test: test.o libfunc
              $(GCC) -o test test.o -L. -lfunc
              %.o:%.c
              $(GCC) -c $(CFLAGS) -o $@ $<
              clean:
              rm -fr .o
              rm -fr .so
              rm -fr test
              要生成.so文件,cc要帶-shared 參數(shù);要調(diào)用.so的文件,比如libfunc.so,可以在cc命令最后加上-lfunc,還要視情況加上-L/usr/xxx 指出libfunc.so的路徑;這樣,在你要編譯的源文件中就可以調(diào)用libfunc.so這個庫文件的函數(shù).
              前面的都說的差不多了,最后提醒一下最好提供一個接口頭文件
              動態(tài)加載,用dlopen,dlclose,dlsym 

            posted @ 2012-12-07 22:21 tqsheng 閱讀(166) | 評論 (0)編輯 收藏

            rpm find

            http://rpm.pbone.net/

            posted @ 2012-12-06 20:22 tqsheng 閱讀(245) | 評論 (0)編輯 收藏

            Visual Studio Team System 2008

            不然你試試 Visual Studio Team System 2008 Team Suite 英文版,它是VS的最高級版本。

            下載地址: http://119.147.41.16/down?cid=D4D18C5498974FF51DE3C26AFBBB4DB08120F633&t=2&fmt=&usrinput=visual%20studio%202008&dt=2004000&ps=0_0&rt=0kbs&plt=0

            破解方法:

            一、先安裝試用版,然后在“添加或刪除程序”里找到VS2008,點“更改/刪除”就會看到一個輸入序列號的地方,把序列號輸進去,點“升級”按鈕即可,Team Suite和Professional通用。

            二、把安裝盤下Setupsetup.sdb文件中的[Product Key]項中對應(yīng)的序列號更改為正式版的序列號后再安裝即可。此方法需要重新打包。 因為九十天試用版本已經(jīng)是rtm版本。所以改變序列號以后的升級或者安裝,就會變成正式版,不再有使用期限。 以下是收集的序列號:

            1.Visual Studio 2008 Professional Edition: XMQ2Y-4T3V6-XJ48Y-D3K2V-6C4WT (本人親自測試,確實可用)

            2.Visual Studio 2008 Team Test Load Agent: WPX3J-BXC3W-BPYWP-PJ8CM-F7M8T

            3.Visual Studio 2008 Team System: PYHYP-WXB3B-B2CCM-V9DX9-VDY8T

            posted @ 2012-12-05 22:07 tqsheng 閱讀(208) | 評論 (1)編輯 收藏

            qt 不錯的blog

            http://www.cnblogs.com/rockhawk/archive/2010/12/15/1906956.html
            http://blog.csdn.net/vbskj/article/details/7792163

            posted @ 2012-12-05 16:59 tqsheng 閱讀(170) | 評論 (1)編輯 收藏

            1 個不錯的howto 網(wǎng)站

            www.ehow.com

            posted @ 2012-12-05 11:20 tqsheng 閱讀(119) | 評論 (0)編輯 收藏

            http://www.xitongzhijia.net/xp/201207/13158.html

            http://www.xitongzhijia.net/xp/201207/13158.html

            posted @ 2012-12-04 18:52 tqsheng 閱讀(488) | 評論 (2)編輯 收藏

            褲子尺碼對照表

            posted @ 2012-11-28 21:30 tqsheng 閱讀(170) | 評論 (0)編輯 收藏

            中科創(chuàng)想視頻會議

            http://www.zkspcn.cn/productlist/4_1.html

             

            聯(lián)系我們

            • 公司總機:+86-010-67832066
            • 全國免費咨詢電話:4008-981-678
            • E-mail:service@zkspcn.cn
            • 地址:北京經(jīng)濟技術(shù)開發(fā)區(qū)同濟中路7號興盛國際9層

            posted @ 2012-11-28 11:03 tqsheng 閱讀(138) | 評論 (0)編輯 收藏

            僅列出標(biāo)題
            共25頁: First 2 3 4 5 6 7 8 9 10 Last 
            国内精品伊人久久久影院| 久久香综合精品久久伊人| 欧美一区二区精品久久| 国产精品久久久久久久久久免费| 久久一区二区免费播放| 狠狠色婷婷久久综合频道日韩 | 精品国产一区二区三区久久| 国产成人久久精品区一区二区| 久久久久久a亚洲欧洲aⅴ | 亚洲va中文字幕无码久久不卡 | 亚洲级αV无码毛片久久精品| 久久久精品免费国产四虎| 久久这里只精品99re66| 99久久99久久| 狠狠综合久久综合88亚洲| 2020最新久久久视精品爱| 丁香色欲久久久久久综合网| 久久精品免费网站网| 国内精品人妻无码久久久影院| 综合久久给合久久狠狠狠97色| 精品一区二区久久久久久久网站| 久久99热这里只频精品6| 精品久久人人做人人爽综合| 国产婷婷成人久久Av免费高清| 狠狠色丁香久久婷婷综合蜜芽五月| 亚洲狠狠久久综合一区77777| 伊人久久大香线蕉综合Av| 久久最新免费视频| 久久久精品视频免费观看| 久久精品国产免费一区| 996久久国产精品线观看| 韩国免费A级毛片久久| 久久精品国产亚洲AV大全| 精品久久久久久国产潘金莲 | 国产午夜电影久久| 国产成人无码精品久久久免费 | 无码日韩人妻精品久久蜜桃| 久久久久久久久久久| 国产精品99久久久精品无码| 久久国产欧美日韩精品| 久久久亚洲欧洲日产国码aⅴ |