歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux如何解決動態庫的版本控制

Linux如何解決動態庫的版本控制

日期:2017/2/28 15:52:48   编辑:Linux教程

(換句話說,soname不是真實存在的文件,只是在此庫中和將來調用此庫的文件中保存的一個名字,在加載時去找這個名字,使用時創建一個軟連接來指向真實文件,這樣真實文件的版本號就可以升級了)

Linux 系統,也同樣面臨和Window一樣的問題,如何控制動態庫的多個版本問題。Window之前沒有處理好,為此專門有個名詞來形容這個問題 “Dll hell”,其嚴重影響軟件的升級和維護。 Dll hell 是指windows 上動態庫新版本覆蓋舊版本,但是卻不兼容老版本。常常發生在程序升級之後,動態庫更新,原有程序運行不起來;或者裝新軟件,但是已有的軟件運行不起來。 同樣Linux操作系統,也有同樣的問題,那麼它是怎麼解決的呢?

Linux 為解決這個問題,引入了一套機制,如果遵守這個機制來做,就可以避免這個問題。 但是這只事一個約定,不是強制的。但是建議遵守這個約定,否則同樣也會出現 Linux 版的Dll hell 問題。 下面來介紹一個這個機制。 這個機制是通過文件名,來控制dll (shared library) 的版本。

Linux 上的Dll ,叫shared library,其有三個名字,分別有不同的目的。

第一個是共享庫本身的文件名(real name),其通常包含版本號,常常是是這樣: libmath.so.1.1.1234 。 lib是Linux 上的庫的約定前綴,math 是共享庫名字,so 是共享庫的後綴名,1.1.1234的是共享庫的版本號,其主版本號+小版本號+build號。主版本號,代表當前動態庫的版本,如果動態庫的接口有變化,那麼這個版本號就要加1;後面的兩個版本號(小版本號 和 build 號)是告訴你詳細的信息,比如為一個hot-fix 而生成的一個版本,其小版本號加1,build號也應有變化。 這個文件名包含共享庫的代碼。

第二個是動態庫的soname( Short for shared object name),其是應用程序加載dll 時候,其尋找共享庫用的文件名。其格式為

lib + math+.so + ( major version number)

其只包含major version number,換句話說,也就是只要其接口沒有變,應用程序都可以用,不管你其後minor build version or build version。

問題來了,程序運行時怎麼通過soname 找個real name? Soname 存在哪裡?如果與real name 關聯起來?什麼時候存的?

這就是接下來要介紹的第三個共享庫的名字,link name,顧名思義,就是在編譯過程,link 階段用的文件名。 其將sonmae 和real name 關聯起來。

第三個名字,共享庫的連接名(link name),是專門為build 階段連接而用的名字。這個名字就是lib + math +.so ,比如libmath.so。其是不帶任何版本信息的。在共享庫編譯過程中,連接(link) 階段,編譯器將生成一個共享庫及real name,同時將共享庫的soname,寫在共享庫文件裡的文件頭裡面。可以用命令 readelf -d sharelibrary 去查看。

在應用程序引用共享庫時,其會用到共享庫的link name。在應用程序的link階段,www.linuxidc.com其通過link名字找到動態庫,並且把共享庫的soname 提取出來,寫在自己的共享庫的頭文件裡面。當應用程序加載時就會通過soname 去給定的路徑下尋找該共享庫。

下面通過這個代碼來說明一下系統是如何做的,並且介紹系統的一些設施和工具:

代碼: 

1. File libhello.c
/* hello.c - demonstrate library use. */
#include <stdio.h>
void hello(void)
{ printf("Hello, library world./n");}
2. File libhello.h
/* libhello.h - demonstrate library use. */
void hello(void);
3. File main.c
/* main.c -- demonstrate direct use of the "hello" routine */
#include "hello.h"
int main(void)
{
hello();
return 0;
}
1.生成共享庫,關聯real name 和soname 。

gcc -g -Wall -fPIC -c hello.c -o hello.o

gcc -shared -W,soname,-libhello.so.0 -o libhello.so.0.0.0 hello.o

將會生成共享庫libhello.so.0.0.0.

可以用系統提供的工具查看共享庫的頭:

readelf -d libhello.so.0.0.0 | grep libhello

ox00000000000e(SONAME) library soname: [libhello.so.0]

2.應用程序,引用共享庫。

先手動生成link 名字,以被後面的程序鏈接時用

ln -s libhello.so.0.0.0 libhello.so.0

gcc -g -Wall -c main.c -o main.o -I.

gcc -o main main.o -lhello -L.

查看編譯出來的程序:

readelf -d main | grep libhello

ox000000000001(NEEDED) shared library: [libhello.so.0]

運行該程序,需要指定共享庫的路徑。 有兩種辦法,第一種使用環境變量“LD_LIBRARY_PATH”. 兩外一種辦法就是將共享庫拷貝到系統目錄(path 環境變量指定的其中一個目錄)。

暫停! 我們還沒有解決一個問題是,程序只知道soname,怎麼從soname 找到共享庫,即real name 文件呢? 這需要我們定義一個link文件,連接到共享庫本身。

ln -s libhello.so.0.0.0 libhello.so.0

當然這個路徑需要放到LD_LIBRARY_PATH環境變量中。

這樣就可以運行該程序。

[Note]Linux 系統提供一個命令 ldconifg 專門為生成共享庫的soname 文件,以便程序在加載時後通過soname 找到共享庫。 同時該命令也為加速加載共享庫,把系統的共享庫放到一個緩存文件中,這樣可以提高查找速度。可以用下面命令看一下系統已有的被緩存起來的共享庫。

ld -p

Copyright © Linux教程網 All Rights Reserved