动态链接和静态链接
cgo项目一般会使用到c/c++源码文件,动态链接库,静态链接库这三种资源文件。 c/c++源码一般放在go的源码目录下,在go代码文件include相应的头文件,go编译的时候会扫描目录下c/c++源码文件进行编译。用c/c++源码编译是最好的情况,但很多情况是没有c/c++源码的。
我们接下来通过编译一个使用了gorocksdb的go程序来了解动态链接库编译和使用静态链接库编译。
新建一个项目rocksdbtest
,目录文件如下:
$ tree
.
├── go.mod
├── go.sum
└── main.go
main.go内容:
package main
import "C"
import (
"fmt"
rockdb "github.com/tecbot/gorocksdb"
"log"
)
const (
FORMAT = "key%08d"
)
var (
wo = rockdb.NewDefaultWriteOptions()
ro = rockdb.NewDefaultReadOptions()
)
func main() {
dir := "/tmp/gorocksdb-TestDBMultiGet"
opts := rockdb.NewDefaultOptions()
opts.SetCreateIfMissing(true)
db, err := rockdb.OpenDb(opts, dir)
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.Put(wo, []byte(fmt.Sprintf(FORMAT, 1)), []byte("test data"))
a, err := db.Get(ro, []byte(fmt.Sprintf(FORMAT, 1)))
defer a.Free()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(a.Data()))
}
要使用gorocksdb
首先需要编译rocksdb,我们参照rocksdb INSTALL中的描述的安装方式安装。我们以ubuntu安装为例:
先安装依赖
- gcc/g++至少要4.8以上以支持c++11
- 安装gflags
sudo apt-get install libgflags-dev
- 安装snappy
sudo apt-get install libsnappy-dev
- 安装zlib
sudo apt-get install zlib1g-dev
- 安装bzip2
sudo apt-get install libbz2-dev
- 安装lz4
sudo apt-get install liblz4-dev
- 安装lz4
sudo apt-get install libzstd-dev
编译rocksdb
$ git clone https://github.com/facebook/rocksdb.git
$ cd rocksdb
$ git checkout v5.18.3 #切换到稳定分支版本
$ make static_lib #编译的静态链接库
$ make shared_lib #编译的动态链接库
$ ll librocksdb*
-rw-r--r-- 1 wida wida 418296002 11月 21 14:31 librocksdb.a #这是编译后的静态链接库
lrwxrwxrwx 1 wida wida 20 7月 29 09:35 librocksdb.so -> librocksdb.so.5.18.3
lrwxrwxrwx 1 wida wida 20 7月 29 09:35 librocksdb.so.5 -> librocksdb.so.5.18.3
lrwxrwxrwx 1 wida wida 20 7月 29 09:35 librocksdb.so.5.18 -> librocksdb.so.5.18.3
-rwxr-xr-x 1 wida wida 121896448 7月 29 09:35 librocksdb.so.5.18.3 #这是编译后的动态链接库
使用动态链接库
我们先编译使用动态链接库的版本。
$ cd rocksdbtest
$ CGO_CFLAGS="-I/home/wida/cppworkspace/rocksdb/include" CGO_LDFLAGS="-L/home/wida/cppworkspace/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd" go build
$ ll -h rocksdbtest #看下编译后的程序大小
-rwxr-xr-x 1 wida wida 2.3M 11月 21 14:34 rocksdbtest
$ ./rocksdbtest
test data
$ ldd rocksdbtest #看依赖的动态库
linux-vdso.so.1 (0x00007ffcef59b000)
librocksdb.so.5.18 => /usr/local/lib/librocksdb.so.5.18 (0x00007f006c187000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f006be06000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f006ba73000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f006b855000)
libbz2.so.1.0 => /lib/x86_64-linux-gnu/libbz2.so.1.0 (0x00007f006b645000)
libsnappy.so.1 => /usr/lib/x86_64-linux-gnu/libsnappy.so.1 (0x00007f006b43d000)
liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f006b220000)
libzstd.so.1 => /usr/lib/x86_64-linux-gnu/libzstd.so.1 (0x00007f006af9c000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f006ad7e000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f006ab7a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f006a7c0000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f006a5b8000)
libgflags.so.2.2 => /usr/lib/x86_64-linux-gnu/libgflags.so.2.2 (0x00007f006a393000)
libnuma.so.1 => /usr/lib/x86_64-linux-gnu/libnuma.so.1 (0x00007f006a188000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0069f70000)
/lib64/ld-linux-x86-64.so.2 (0x00007f006c925000)
从上面的编译结果来看,使用动态链接库编译出来的可执行文件比较小,但是依赖的动态库比较多,这样子带发布程序的时候你需要同时发布依赖的动态库。而且动态库还要保证版本兼容性,碰到不兼容的情况处理起来就相当棘手。
使用静态链接库
使用静态链接库编译也是比较理想的cgo编译选项。编译后的可执行文件也只有一个,不会依赖其他动态链接库,这样对程序部署或者容器化非常有帮助。
使用全静态编译:
$ cd rocksdbtest
$ CGO_CFLAGS="-I/home/wida/cppworkspace/rocksdb/include" CGO_LDFLAGS="-L/home/wida/cppworkspace/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd -linkmode "external" -extldflags "-static"" go build
$ ll -h rocksdbtest
-rwxr-xr-x 1 wida wida 42M 11月 21 14:44 rocksdbtest #这个演示的程序比较小,有时候编译出来可能有上百M
$ ./rocksdbtest
test data
$ ldd rocksdbtest #看依赖的动态库
不是动态可执行文件
编译后的可执行文件只有一个rocksdbtest
,ldd
命令看它的依赖发现不是动态可执行文件
也就是说没有依赖动态链接库。
总结
本小节介绍了cgo程序编译使用动态链接库和静态链接库的使用。最优先的情况是使用源码编译,静态链接库次之,最后是使用动态链接库。