Golang连接PostgreSQL数据库时出现段错误问题
Golang连接PostgreSQL数据库时出现段错误问题
我在尝试连接(PQconnectdb)PostgreSQL数据库时遇到了应用程序问题。当我在连接数据库后不调用任何函数时,一切正常,我可以与数据库通信。但是,例如,如果在连接后调用函数MainApp.Init(),应用程序就会挂起(段错误)。
问题出在哪里?
使用gdb调试时,gdb返回错误:Thread 1 “CameraModule” received signal SIGSEGV, Segmentation fault. 0x0000007fb6e92918 in strcmp () from /lib/aarch64-linux-gnu/libc.so.6
示例代码: …
LOGINFO("PQconnectdb\n");
ConnectionPtr pConnection = PQconnectdb( szconn.c_str() ); //连接数据库
MainApp.Init() //调用此函数时出现段错误
更多关于Golang连接PostgreSQL数据库时出现段错误问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我使用来自C语言库的PostgreSQL。使用的函数是PostgreSQL在C语言中编译,并与Go混合调用。 问题仅在于,当我在连接后调用Go中的某些函数时,应用程序会挂起。 如果我在连接后不调用任何函数,一切正常。
func main() {
fmt.Println("hello world")
}
更多关于Golang连接PostgreSQL数据库时出现段错误问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好 @Przemyslaw_Jakobczak,欢迎来到 Go 论坛。
我有点困惑。PQconnectdb 是来自 C 库 libpq 的一个函数。这个错误也源自一个 C 库 libc,并且示例代码是 C 代码。
你的问题是如何与 Go 关联起来的呢?
奇怪,我在函数 PQconnectdb 中添加了参数 sslmode=disable,现在可以正常工作了!
PQconnectdb("user='cam' password='cam' host='127.0.0.1' dbname='cpanel' port='5432' sslmode=disable")
感谢帮助。
所以,只要在建立数据库连接后,Go代码不再调用任何C函数,C部分就能正常工作,是吗?
你提到了互斥锁,所以我猜你的Go代码运行了多个goroutine。libpq文档指出,libpq是否线程安全取决于编译时使用的选项。如果你调用PQisthreadsafe(),它会返回1吗?
另外,我对strcmp()中的SEGV(段错误)感到疑惑,这似乎表明某个字符串没有正确地以空字符结尾。(但另一方面,c_str()应该能确保这一点,对吧?)uri是一个有效的连接字符串吗?它是否在经历所有转换(C.Cstring()、c_str())后依然保持有效?
由于需要与其他应用程序保持代码兼容性,我必须在C语言中使用PostgreSQL接口。
奇怪的是,当我在Go应用程序中调用某些函数时,程序会挂起,而且这种情况只在连接到数据库后发生。为什么当我不在Go应用程序中调用任何函数时,一切正常?我尝试在连接时使用互斥锁,但同样出现了错误。
//----------------------------------------------------------------------
// Go代码调用C
func InitConnections(uri string) {
C.InitCPanelInterface(C.CString(uri), C.int(5))
}
// C语言代码
void InitCPanelInterface( const char* connString, int size )
{
using namespace ltm;
ConnectionPool* pConn = ConnectionPool::instance();
InitializationFunc func( &CPanelQueries::init );
pConn->init( connString, size, func );
}
void ConnectionPool :: init( const std::string& szconn, unsigned int size,
InitializationFunc func )
{
using namespace std;
LOGINFO("Initialize connection with string '%s'\n", szconn.c_str() );
for( unsigned int i = 0; i < size; i++ )
{
LOGINFO("PQconnectdb\n");
ConnectionPtr pConnection = PQconnectdb( szconn.c_str() ); //在此处连接,应用程序挂起
LOGINFO("if( PQstatus(...\n");
if( PQstatus( pConnection ) != CONNECTION_OK )
{
pConnection = nullptr;
LOGERROR( "Init connection [%d] failed\n", i );
throw runtime_error( "Connection failure" );
}
LOGINFO("push()\n");
push( pConnection );
这是一个典型的CGO内存管理问题。段错误发生在strcmp函数中,表明有内存访问越界或空指针解引用。问题很可能出现在MainApp.Init()函数内部使用了不正确的C内存管理。
根本原因:
当通过CGO调用C函数(如PQconnectdb)返回的ConnectionPtr在Go代码中使用时,如果这个指针被传递给其他C函数,或者在Go和C之间传递时生命周期管理不当,就会导致段错误。
示例代码分析:
package main
/*
#include <stdlib.h>
#include <libpq-fe.h>
*/
import "C"
import (
"unsafe"
"log"
)
// 错误的做法 - 可能导致段错误
func connectDBWrong(connStr string) *C.PGconn {
cstr := C.CString(connStr)
defer C.free(unsafe.Pointer(cstr))
conn := C.PQconnectdb(cstr)
// 这里立即释放了cstr,但conn可能还在内部使用这个字符串
return conn
}
// 正确的做法
func connectDBRight(connStr string) *C.PGconn {
cstr := C.CString(connStr)
conn := C.PQconnectdb(cstr)
C.free(unsafe.Pointer(cstr)) // 确保在连接建立后释放
if C.PQstatus(conn) != C.CONNECTION_OK {
errMsg := C.GoString(C.PQerrorMessage(conn))
C.PQfinish(conn)
log.Fatal("Connection failed:", errMsg)
}
return conn
}
// MainApp.Init() 中可能的问题
func (app *MainApp) Init() {
// 如果这样使用C指针,可能导致问题
// var someCData *C.some_type
// 需要确保C内存的正确管理
// 正确的CGO内存管理示例
query := "SELECT * FROM table"
cquery := C.CString(query)
defer C.free(unsafe.Pointer(cquery))
// 执行查询等操作
}
关键点:
- C字符串生命周期:使用
C.CString创建的字符串必须手动释放 - 指针传递:确保C指针在Go中正确传递,避免被Go的垃圾回收器误处理
- 线程安全:PostgreSQL连接可能涉及线程局部存储
调试建议:
在MainApp.Init()函数中添加分段调试,检查所有CGO调用点的内存管理:
func (app *MainApp) Init() {
log.Println("Init step 1")
// 第一步操作
log.Println("Init step 2")
// 第二步操作
// 逐步缩小问题范围
}
检查MainApp.Init()中是否涉及:
- 直接操作C指针
- 未正确管理的C内存分配
- 跨Go/C边界的数据结构传递
- 可能触发垃圾回收的操作
使用runtime.KeepAlive()确保C指针在需要时不被回收:
func (app *MainApp) Init() {
conn := connectDBRight("dbname=test")
defer C.PQfinish(conn)
// 确保conn在函数执行期间不被回收
runtime.KeepAlive(conn)
// 其他操作...
}

