LoginSignup
13
15

More than 5 years have passed since last update.

Go言語から共有ライブラリ(so)をロードして使う

Last updated at Posted at 2016-10-22

Go言語でプラグインの機構を使いたい

やりたいこととしては、下記の通り。

  • LinuxにGoで建てたHTTPサーバをデーモンとして動かしたい
  • HTTPサーバはプラグインを読み込んで機能の拡充が行える
  • HTTPサーバは設定ファイルにプラグインファイルのパスを追加するだけで機能が拡充出来る
  • プラグインを追加する度に本体をリビルドするのは嫌だ

どう実装するか

最初はHashiCorpが出しているGo言語でプラグインの機構を備えるライブラリを見ていたけども・・・。
https://github.com/hashicorp/go-plugin

このプラグインは、読み込むプラグインの数だけプロセスが立ち上がってしまう。
※何故ならば、このプラグインはRPCを使ってプラグインと疎通する設計のため、プラグイン側もプロセスを立ち上げておかないといけない。

じゃあどうするか

稼働させる場所は、Linuxサーバと決まっているため、もう共有ライブラリで良いんじゃね!?と思った次第。
共有ライブラリもビルド出来るしcgoを使えば何とかなりそう。

検証コード

共有ライブラリ(.so)側

hello関数を呼び出すと標準出力にhelloを表示するだけのどシンプルなものでございます。

hello.go
package main

import "C"
import "fmt"

//export hello
func hello() {
    fmt.Println("hello")
}

func main() {
}

共有ライブラリをロードして使う側

main.go
package main
/*
#cgo LDFLAGS: -ldl
#include <dlfcn.h>

void call_func(void* p) {
    void (*func)() = p;
    func();
}
*/
import "C"

func main() {
    handle := C.dlopen(C.CString("./shared/hello.so"), C.RTLD_LAZY)
    if handle == nil {
        panic("Failed to open shared object.")
    }
    defer C.dlclose(handle)

    hello := C.dlsym(handle, C.CString("hello"))
    if hello == nil {
        panic("Could not found function pointer.")
    }
    C.call_func(hello)
}

検証してみる

共有ライブラリのビルド

$ mkdir shared
$ cd shared
$ go build -buildmode=c-shared -o hello.so hello.go

呼び出し側の確認

$ go run main.go
$ hello

ちゃんとhelloが標準出力に表示されていることを確認出来た。
一応Goでプラグインコードの実装が出来るからこれでも良いよね・・・。

13
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
15