Go中使用動態庫C/C++庫


轉自:http://studygolang.com/articles/1441

最近需要做一些在go中使用動態C++庫的工作,經常碰到找不到動態庫路徑這種情況,所以就花點時間,專門做一下實驗來了解Go。

 

一、示例代碼目錄結構(假設代碼根目錄為/home/gdc/cgotest):

----|bin:
----|pkg
----|src
--------|main
------------|main.go
--------|oidb
------------|hello
----------------|hello.go:
----------------|api.h
------------|lib
----------------|libapi.so

二、代碼
api.h文件內容:

#ifndef __API_H__
#define __API_H__
void hello();
#endif

hello.go文件內容:

package hello
/*
#cgo CFLAGS: -I./include
#cgo LDFLAGS: -L../lib -lapi
#include "api.h"
*/
import "C"

func Hello(){
C.hello();
}

main.go文件內容:

package main
import "oidb/hello"
func main(){
hello.Hello();
}


三、前提

A. 將該代碼目錄添加到GOPATH環境變量中。——export GOPATH=$GOPATH:/home/gdc/cgotest

B. 環境變量LD_LIBRARY_PATH值為空



四、關於cgo中使用動態C/C++庫的一些小實驗:

A. 按照如上的目錄組織及文件內容,可以在任意地方執行go build oidb/hello或者 go install oidb/hello。

執行go build oidb/hello這個命令,可能會沒啥反應,但是可以告訴你編譯通過了。

執行go install oidb/hello這個命令,會在代碼根目錄下的pkg/linux_amd64目錄(如果不存在會自動創建)創建oidb/hello.a。

OK,說明該包已經可以給別人使用了。但是別人能否正常使用呢?接下來做一些main.go相關實驗。

 

B. 很遺憾,此時我在很多位置嘗試執行go build main 或者 go install main,都會提示我找不到libapi.so動態庫。

但有趣的是,當我在libhello目錄下執行go build main 或者 go install main時,都會成功。其中執行go install main會在代碼根目錄的bin子目錄下生成

一個main可執行文件,go build main會在當前目錄下生成一個main可執行程序。然后當我嘗試執行這個位於bin目錄下的main可執行文件后,會提示找

不到libapi.so這個動態庫。

然后我又嘗試將lib目錄復制到bin目錄下和代碼根目錄下,然后在bin子目錄下執行main可執行文件,還是提示找不到libapi.so動態庫。

最后,我拿出終極大招,執行"export LD_LIBRARY_PATH=/home/gdc/cgotest/lib"(此時我將lib目錄移動到代碼根目錄下了),然后就可以正常

運行main可執行程序了。

然后我又將該lib目錄移動到bin目錄和src目錄下,然后分別用export命令將這個lib目錄的新位置添加到LD_LIBRARY_PATH環境變量中,然后執行main,都可以正常執行。

 

小結:

main包的編譯理解:

當編譯或安裝main包時,go會以此時所處的位置為當前目錄。然后如果其引用的某個包中使用相對目錄依賴某個動態庫C/C++庫。那么會從當前目錄出發,根據那個相對位置去找動態庫。所以上面在編譯或安裝main包時,唯獨在lib或hello目錄下成功通過編譯了,這就是因為go以從當前目錄出發,到其父目錄的lib子目錄下尋找libapi.so動態庫,然后成功找到,從而通過編譯。其實不一定非要在lib或者hello目錄下編譯main包。只要確保編譯main包位置的父目錄下有一個子目錄lib,同時該lib目錄下有libapi.so這個文件,即可通過編譯。比如,我將lib目錄在代碼根目錄下,然后我轉到bin目錄下,然后就可以成功編譯或安裝main包了。

當然了,如果你在使用動態庫時使用"-L"選項設置的是絕對目錄而不是相對目錄,那么編譯或安裝main包,就沒有這么多限制了。你可以在任意位置編譯或安裝。


main可執行程序執行的理解:
不管你在代碼中使用"-L"選項指定的動態庫位置是相對目錄或絕對目錄,要想執行這個main可執行程序,都要將所依賴的動態庫所在的目錄添加到環境變量LD_LIBRARY_PATH中(或者將動態庫拷貝到系統默認的庫搜索路徑下,但是老大不允許啊,郁悶)。

C. 下面,再做一些關於直接編譯main.go文件的實驗吧。
這個的編譯與編譯main包一樣的——"-L"使用相對路徑,那么必須確保編譯的位置在加上相對路徑能最終找到動態庫;"-L"使用絕對路徑,則可以在任意位置編譯。

D. 最后,再做一些關於go中設置環境變量的實驗。因為main程序的執行需要依賴LD_LIBRARY_PATH這個環境變量的值。
D1. 第一種方法:魏老大說的,就是寫一個shell腳本,在這個腳本中先執行export語句,將動態庫的路徑加入到LD_LIBRARY_PATH中。然后再運行程序。
OK。這個方法通過。

D2. 第二種方法:在go中設置環境變量的值。

經過自己實驗,然后問魏老大,感覺這是不可能的。因為當程序啟動時,系統就會自動加載該程序所依賴的那些庫,而此時你在程序中設置環境變量的

代碼還沒運行呢。當然還是找不到動態庫。

一個解決辦法:自己手動加載動態庫。

參考了http://blog.csdn.net/joker0910/article/details/6103793這篇文章的手動加載庫,可以正常使用。

 

F. 補充(2014.07.21)。
忘記做go test hello這個實驗了。經實驗,發現假如在該包中的-L使用相對目錄來定位動態庫,那么要想成功執行這個命令,需要以下兩點:
第一,要確保執行該命令的位置再加上相對目錄所得到的目錄需要包含所依賴的動態庫。這個與編譯或安裝main包很像。
第二,需要將所依賴的動態庫所在的目錄添加到環境變量LD_LIBRARY_PATH中。這個與執行main很像。


修改后的hello.go文件內容如下:

package hello

/*
#cgo LDFLAGS: -ldl
#include 
#include 
#include "api.h"

void hello_c(char* lib_path){
        char* func_name="hello";
        void* libc;
        void (*hello_call)();
        if(libc = dlopen(lib_path,RTLD_LAZY))
        {
                hello_call = dlsym(libc, func_name);
                (*hello_call)();
                dlclose(libc);
        }
}
*/
import "C"
import "unsafe"

var EXTENSION_DIR string = "/home/guess/.davengu_workdir/go_learning/cgo/use_shared_library/src/oidb/lib/"
var OIDB_API string = "libapi.so"

//#cgo LDFLAGS: -L/home/guess/.davengu_workdir/go_learning/cgo/use_shared_library/src/oidb/lib -lapi
//#cgo LDFLAGS: -L../lib -lapi

func Hello() {
        libPathC := C.CString(EXTENSION_DIR+OIDB_API);
        defer C.free(unsafe.Pointer(libPathC));
        C.hello_c(libPathC);

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM