大家: 我写了一个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'时,后台进程也会正常执行,而不是阻塞。
我不知道为什么,但我现在已经解决了。 go编译动态库“libtest1.so”,从编译时用-ltest1命令开始,到在load_so_main()函数中运行,通过dlopen()打开libtest1.so,然后通过dlsym()找到Test1()函数,成功了正确地。