用golang编写的代码,输出为C动态库,被posgres17调用,但是Golang生成的动态库函数被屏蔽了

问题描述 投票:0回答:1

大家: 我写了一个demo,但是不能正常运行,你能帮我吗,非常感谢。

golang编写的代码,输出为C动态库“libtest1.so”,被posgres17编写的“load_so_test.so”注册后台进程调用(12版本也有问题),“中的Test1()”函数当在 postgresql.conf 的共享预加载库中配置“load_so_test.so”时,“libtest1.so”会阻塞。

但是当“load_so_test.so”注册了一个带有create扩展的函数并使用该函数启动后台进程时,“libtest1.so”中的“Test1()”函数正常执行。

整体调用逻辑为: pg -> load_so_test.so(注册一个后台进程并调用libtest1.so) -> libtest1.so (导出一个Test1()函数供load_so_test.so调用)

测试环境: OS:CentOS9(CentOS7 Ubuntu22.04也测试过,同样的问题) golang: go1.23.3 linux/amd64 posgres:posgres17

这是用于测试的所有代码。

走吧

package main

import "C"
import (
        "fmt"
)

//export Test1
func Test1() {
        ch := make(chan int)
        go func() {
                ch <- 10
                fmt.Println("in go routine")
        }()
        val := <-ch
        fmt.Printf("get val is %d\n", val)
}

func main() {
        Test1()
}

执行命令:

go build -buildmode=c-shared -o libtest1.so main.go

生成libtest1.h和libtest1.so文件。

页面代码:

在 contrib 中创建一个 load_so_test 目录,其中包含以下文件:

├── libtest1.h  ---go code generation file
├── libtest1.so ---go code generation file
├── load_so_test--1.0.sql
├── load_so_test.c
├── load_so_test.control
└── Makefile

load_so_test--1.0.sql:

\echo Use "CREATE EXTENSION load_so_test" to load this file. \quit

CREATE FUNCTION load_so_launch()
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;

load_so_test.c(创建后台进程有两种方式):


#include "postgres.h"

/* These are always necessary for a bgworker */
#include "miscadmin.h"
#include "postmaster/bgworker.h"
#include "postmaster/interrupt.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
#include "storage/shmem.h"

/* these headers are used by this particular worker's code */
#include "access/xact.h"
#include "commands/dbcommands.h"
#include "executor/spi.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "pgstat.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/snapmgr.h"

#include "libtest1.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(load_so_launch);

PGDLLEXPORT void load_so_main(Datum main_arg) pg_attribute_noreturn();

static uint32 worker_spi_wait_event_main = 0;

void load_so_main(Datum main_arg)
{
    /* Establish signal handlers before unblocking signals. */
    pqsignal(SIGHUP, SignalHandlerForConfigReload);
    pqsignal(SIGTERM, die);

    /* We're now ready to receive signals */
    BackgroundWorkerUnblockSignals();

    //golang exported function
    Test1();

    for (;;)
    {
        if (worker_spi_wait_event_main == 0)
            worker_spi_wait_event_main = WaitEventExtensionNew("LoadSoTest");

        (void)WaitLatch(MyLatch,
                        WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
                        3000L,
                        worker_spi_wait_event_main);
        ResetLatch(MyLatch);
        ereport(LOG, (errmsg("load_so_main is running")));
    }
}

void _PG_init(void)
{
    BackgroundWorker worker;
    if (!process_shared_preload_libraries_in_progress)
        return;
    memset(&worker, 0, sizeof(worker));
    worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
                       BGWORKER_BACKEND_DATABASE_CONNECTION;
    worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
    worker.bgw_restart_time = BGW_NEVER_RESTART;
    sprintf(worker.bgw_library_name, "load_so_test");
    sprintf(worker.bgw_function_name, "load_so_main");
    snprintf(worker.bgw_name, BGW_MAXLEN, "load_so_test worker");
    snprintf(worker.bgw_type, BGW_MAXLEN, "load_so_test");
    worker.bgw_notify_pid = 0;
    RegisterBackgroundWorker(&worker);
}

Datum load_so_launch(PG_FUNCTION_ARGS)
{
    BackgroundWorker worker;
    BackgroundWorkerHandle *handle;
    BgwHandleStatus status;
    pid_t pid;
    memset(&worker, 0, sizeof(worker));
    worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
                       BGWORKER_BACKEND_DATABASE_CONNECTION;
    worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
    worker.bgw_restart_time = BGW_NEVER_RESTART;
    sprintf(worker.bgw_library_name, "load_so_test");
    sprintf(worker.bgw_function_name, "load_so_main");
    snprintf(worker.bgw_name, BGW_MAXLEN, "load_so_test dynamic worker");
    snprintf(worker.bgw_type, BGW_MAXLEN, "load_so_test dynamic");
    worker.bgw_main_arg = Int32GetDatum(0);
    /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */
    worker.bgw_notify_pid = MyProcPid;

    if (!RegisterDynamicBackgroundWorker(&worker, &handle))
        PG_RETURN_NULL();

    status = WaitForBackgroundWorkerStartup(handle, &pid);

    if (status == BGWH_STOPPED)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
                 errmsg("could not start background process"),
                 errhint("More details may be available in the server log.")));
    if (status == BGWH_POSTMASTER_DIED)
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
                 errmsg("cannot start background processes without postmaster"),
                 errhint("Kill all remaining database processes and restart the database.")));
    Assert(status == BGWH_STARTED);

    PG_RETURN_INT32(pid);
}

load_so_test.control:

# load_so_test extension
comment = 'load_so_test test'
default_version = '1.0'
module_pathname = '$libdir/load_so_test'
relocatable = true

生成文件:

# contrib/load_so_test/Makefile

MODULES = load_so_test

EXTENSION = load_so_test
DATA = load_so_test--1.0.sql

PG_CFLAGS = -L. -ltest1

ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/load_so_test
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

运行make install后,现在需要手动将libtest1.so复制到pg安装目录的lib/目录下。

在 postgresql.conf 文件中设置shared_preload_libraries = 'load_so_test'时运行此命令。

./bin/pg_ctl -D ./data start -l 日志文件

Test1()在load_so_main函数中执行。发生阻塞。

如果没有设置shared_preload_libraries。相反,要创建扩展并通过函数动态创建后台进程,则在load_so_main函数中执行Test1();可以正常执行了。

create extension load_so_test;
select load_so_launch();

现在我想要的结果是,当配置了shared_preload_libraries = 'load_so_test'时,后台进程也会正常执行,而不是阻塞。

非常感谢。

现在我想要的结果是,当配置了shared_preload_libraries = 'load_so_test'时,后台进程也会正常执行,而不是阻塞。

postgresql go blocking cgo
1个回答
0
投票

我不知道为什么,但我现在已经解决了。 go编译动态库“libtest1.so”,从编译时用-ltest1命令开始,到在load_so_main()函数中运行,通过dlopen()打开libtest1.so,然后通过dlsym()找到Test1()函数,成功了正确地。

© www.soinside.com 2019 - 2024. All rights reserved.