When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed.
funcinit() { log.Println("main package init function called") }
funcmain() { log.Println("main function stared") // load module 插件您也可以使用go http.Request从远程下载到本地,在加载做到动态的执行不同的功能 // 1. open the so file to load the symbols plug, err := plugin.Open("./plugin_doctor.so") if err != nil { fmt.Println(err) os.Exit(1) } log.Println("plugin opened")
// 2. look up a symbol (an exported function or variable) // in this case, variable Greeter doc, err := plug.Lookup("Doctor") if err != nil { fmt.Println(err) os.Exit(1) }
// 3. Assert that loaded symbol is of a desired type // in this case interface type GoodDoctor (defined above) doctor, ok := doc.(GoodDoctor) if !ok { fmt.Println("unexpected type from module symbol") os.Exit(1) }
// 4. use the module if err := doctor.HealthCheck(); err != nil { log.Println("use plugin doctor failed, ", err) } }
4.3 build plugin程序
1 2 3 4 5 6 7 8 9 10 11
pi@homePi:/data/felix/plugin $ go build use_plugin_example.go pi@homePi:/data/felix/plugin $ ll 总用量 6300 -rw-r--r-- 1 pi pi 612 9月 6 17:08 plugin_bad_docter.go -rw-r--r-- 1 pi pi 3493654 9月 6 17:06 plugin_doctor.so -rw-r--r-- 1 pi pi 274 9月 6 16:37 readme.md -rwxr-xr-x 1 pi pi 2941503 9月 6 17:15 use_plugin_example -rw-r--r-- 1 pi pi 1057 9月 6 2019 use_plugin_example.go
pi@homePi:/data/felix/plugin $ file use_plugin_example use_plugin_example: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=fc60641527d9b030f9f4d5a477de300e9fb70541, not stripped
4.4 go run
1 2 3 4 5 6 7
pi@homePi:/data/felix/plugin $ ./use_plugin_example 2019/09/06 17:16:23 main package init function called 2019/09/06 17:16:23 main function stared 2019/09/06 17:16:23 plugin init function called 2019/09/06 17:16:23 plugin opened 2019/09/06 17:16:23 now is 2019-09-06T17:16:23+08:00 2019/09/06 17:16:23 shell has executed ->>>>> golang plugin remote shell bash arg000 arg001
5.Go语言plugin局限和不足
Go plugin 还不是一个成熟的解决方案.它迫使您的插件实现与主应用程序产生高度耦合.即使您可以控制插件和主应用程序, 最终结果也非常脆弱且难以维护.如果插件的作者对主应用程序没有任何控制权,开销会更高.
go run xxx.go //直接运行xxx.go,有点类似解释语言,比如lua, python,其实执行go run,是对源文件做了build,然后再run执行文件的,只是这些都在后台做了
go build xxx.go //编译产生同名的执行档,如果在源文件目录下直接go build会产生与目录名同名的可执行档 go install xxx.go //也是编译,只是编译后会将同名的执行档安装到$GOPATH/bin下面,同样在源文件目录下直接go install,会把与目录同名的可执行档安装在$GOPATH/bin下
以上的go build都是默认的编译,也就是-buildmode是default的
By default, listed main packages are built into executables and listed non-main packages are built into .a files.
-buildmode=shared : Golang生成Golang可调用的动态库so
现在进入正题,这里我主要是要说明下面这个选项的使用方法:
shared: Combine all the listed non-main packages into a single shared library.
我们先看一个hello world的例子:
1 2 3 4 5 6 7
package main
import"fmt"
int main(){ fmt.Println("hello world") }
这里的fmt就是一个share library,这是go里面自带的,
现在如果想自定义一个共享库,应该怎么做? 假设在$GOPATH/src下有:
1 2 3 4
--myAdd --add.go --myCall --main.go
详细目录如下:
1 2 3 4 5 6 7 8
Jermine@Jermine-VirtualBox:~/mygo/src$ echo $GOPATH /home/Jermine/mygo Jermine@Jermine-VirtualBox:~/mygo$ ls bin pkg src Jermine@Jermine-VirtualBox:~/mygo/src$ ls myAdd add.go Jermine@Jermine-VirtualBox:~/mygo/src$ ls myCall main.go
Before compile any shared library, the standard builtin packages should be installed as shared library. This will allow any other shared library to link with them. 意思是在编译任何共享包前,先要把官方自身的标准包先以共享包方式安装下,这样的话其他的包就可以与它们做link。
下面就按官方要求先来编译一下:
1
~/mygo/src/myAdd$ go install -buildmode=shared -linkshared std
Jermine@Jermine-VirtualBox:~/mygo/src$ go install -buildmode=shared -linkshared myAdd Jermine@Jermine-VirtualBox:~/mygo/src$ ls github.com golang.org myAdd test Jermine@Jermine-VirtualBox:~/mygo/src$ ls myAdd/ add.go //这里并没有产生任何文档,产生在那里接着向下看
实际产生共享是在GOPATH/pkg目录下:
1 2 3 4 5 6
Jermine@Jermine-VirtualBox:~/mygo$ ls bin pkg src Jermine@Jermine-VirtualBox:~/mygo$ ls pkg linux_386 linux_386_dynlink Jermine@Jermine-VirtualBox:~/mygo$ ls pkg/linux_386_dynlink/ libmyAdd.so myAdd.a myAdd.shlibname //这些库在编译时已经链接到标准std库,所以下面在应用程序中可以直接使用。
下面是应用程序的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Jermine@Jermine-VirtualBox:~/mygo/src$ vim myCall/main.go package main import ( "fmt" "myAdd"//这就是上面自定义package,如果这里不import,只要下面用了包里面的函数,gofmt也会自动把这个包加进来,是不是有点跟使用fmt等其他包一样的用法 ) funcmain() { fmt.Println("my Call application") fmt.Printf("Sum: %d\n", myAdd.Sum(2, 3)) }
Jermine@Jermine-VirtualBox:~/mygo/src$ ./main my Call application Sum: 5
Jermine@Jermine-VirtualBox:~/mygo/src/mylib$ ls exptest.go
Jermine@Jermine-VirtualBox:~/mygo/src/mylib$ vim exptest.go package main import"C" import"fmt" //export Summ funcSumm(x, y int)int { return x + y } //export Hello funcHello(str string) { fmt.Printf("Hello: %s\n", str) } funcmain() { // We need the main function to make possible // CGO compiler to compile the package as C shared library }
官方说法:
Go functions can be executed from C applications. They should be exported by using the following comment line: //export
typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; typedef unsigned short GoUint16; typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoInt64 GoInt; typedef GoUint64 GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; typedef float _Complex GoComplex64; typedef double _Complex GoComplex128;
/* static assertion to make sure the file is being used on architecture at least with matching size of GoInt. */ typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];