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

7 回复

我使用来自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 的 Postgres 包(例如 pqpgx),从而避免与 C 语言接口的麻烦?

您当前的问题看起来像是从 Go 调用 C 时出现的问题。您能分享一下调用 C 函数的 Go 代码吗?或者更好的话,提供一个最小化的版本?

所以,只要在建立数据库连接后,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))
    
    // 执行查询等操作
}

关键点

  1. C字符串生命周期:使用C.CString创建的字符串必须手动释放
  2. 指针传递:确保C指针在Go中正确传递,避免被Go的垃圾回收器误处理
  3. 线程安全: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)
    
    // 其他操作...
}
回到顶部