Golang中动态库加载与卸载的实现方法

Golang中动态库加载与卸载的实现方法 我在Go语言中创建了一个共享库,并在一个Go应用程序中使用了这个库。我正在寻找这个库加载/卸载时调用的函数。库的init()函数不起作用。通过使用import "C",我在库中编写了以下代码:

package testlibrary

/*
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
static void con() __attribute__((constructor));
void con() {
fprintf(stderr,"I'm a constructor\n");
}
*/

import "C"
import "fmt"

func init() {
fmt.Println("init never called")
}

但我从未收到con()函数中的消息。 有人能建议一下库加载时调用的函数吗?


更多关于Golang中动态库加载与卸载的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中动态库加载与卸载的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,共享库的加载和卸载可以通过cgo结合C语言的构造函数/析构函数特性来实现。你遇到的问题可能是由于Go的运行时初始化顺序导致的。以下是正确的实现方法:

1. 共享库代码示例

// testlibrary.go
package main

import "C"
import (
    "fmt"
    "sync"
)

// 导出函数必须放在main包中
//export Hello
func Hello() {
    fmt.Println("Hello from shared library")
}

var (
    cleanupOnce sync.Once
)

// 构造函数 - 在库加载时调用
//export library_init
func library_init() {
    fmt.Println("Library loaded - constructor called")
}

// 析构函数 - 在库卸载时调用
//export library_cleanup
func library_cleanup() {
    cleanupOnce.Do(func() {
        fmt.Println("Library unloaded - destructor called")
    })
}

// C构造函数声明
/*
#include <stdio.h>
#include <stdlib.h>

// 声明Go函数
extern void library_init(void);
extern void library_cleanup(void);

// 使用GCC/Clang的构造函数属性
__attribute__((constructor)) static void init_library() {
    fprintf(stderr, "C constructor called\n");
    library_init();
}

__attribute__((destructor)) static void cleanup_library() {
    fprintf(stderr, "C destructor called\n");
    library_cleanup();
}
*/
import "C"

func init() {
    // Go的init函数在共享库中可能不会被调用
    fmt.Println("Go init function - may not be called in shared library")
}

func main() {
    // 必须有一个空的main函数
}

2. 编译共享库

# 编译为C共享库
go build -buildmode=c-shared -o libtest.so testlibrary.go

3. 使用共享库的应用程序

// main.go
package main

/*
#cgo LDFLAGS: -L. -ltest -Wl,-rpath,.
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

typedef void (*hello_func)();

// 动态加载函数声明
void* load_library(const char* path);
void unload_library(void* handle);
void call_hello(void* handle);
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    // 动态加载共享库
    libPath := C.CString("./libtest.so")
    defer C.free(unsafe.Pointer(libPath))
    
    handle := C.load_library(libPath)
    if handle == nil {
        fmt.Println("Failed to load library")
        return
    }
    
    // 调用库函数
    C.call_hello(handle)
    
    // 卸载库
    C.unload_library(handle)
    fmt.Println("Library unloaded")
}

4. C辅助函数实现

// library_loader.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

typedef void (*hello_func)();

void* load_library(const char* path) {
    void* handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
    if (!handle) {
        fprintf(stderr, "Error loading library: %s\n", dlerror());
        return NULL;
    }
    printf("Library loaded successfully\n");
    return handle;
}

void unload_library(void* handle) {
    if (handle) {
        dlclose(handle);
        printf("Library unloaded\n");
    }
}

void call_hello(void* handle) {
    hello_func hello = (hello_func)dlsym(handle, "Hello");
    if (hello) {
        hello();
    } else {
        fprintf(stderr, "Error finding Hello function: %s\n", dlerror());
    }
}

5. 编译和运行

# 编译共享库
go build -buildmode=c-shared -o libtest.so testlibrary.go

# 编译C辅助代码
gcc -c -fPIC library_loader.c -o library_loader.o

# 编译主程序
go build -o main main.go library_loader.o -ldl

# 运行程序
./main

关键点说明:

  1. 构造函数/析构函数属性:使用__attribute__((constructor))__attribute__((destructor))是正确的方法
  2. 导出函数:共享库函数必须使用//export注释导出
  3. main包:共享库代码必须在main包中
  4. 动态加载:使用dlopen()/dlclose()进行动态加载卸载
  5. 初始化顺序:C构造函数会在库加载时立即执行,Go的init()函数可能不会在共享库上下文中被调用

运行此代码时,你会看到构造函数和析构函数的输出信息,确认库的加载和卸载事件被正确捕获。

回到顶部