Golang中如何解决goroutine运行时错误

Golang中如何解决goroutine运行时错误 你好,

我在执行我的Go应用程序时遇到了一个错误。Go应用程序调用了一个插件方法,并因以下堆栈信息而崩溃。有人能帮忙修复这个错误吗?

runtime: unexpected return pc for testmethod called from 0x7fdda7006de0
stack: frame={sp:0xc000d09850, fp:0xc000d09858} stack=[0xc000d04000,0xc000d0c000)
000000c000d09750:  00007fddb3e17d2f <runtime.cgocallbackg+239>  00007fddb3e17ba5 <runtime.cgocall+101>
000000c000d09760:  000000c000d097e8  000000c000d097a8
000000c000d09770:  00007fddb3e67e56 <runtime.exitsyscallfast+214>  000000c000177000
000000c000d09780:  000000c000d097b8  00007fddb3e674de <runtime.reentersyscall+126>
000000c000d09790:  000000c0012ead80  0000000300000002
000000c000d097a0:  000000c0012ead80  000000c000d097d8
000000c000d097b0:  00007fddb3e67cc9 <runtime.exitsyscall+393>  000000c000d097d8
000000c000d097c0:  00007fddb3e676e6 <runtime.entersyscall+38>  00007fddb3e17ba5 <runtime.cgocall+101>
000000c000d097d0:  000000c000d097e8  00007fddb3e9c7ff <runtime.asmcgocall+63>
000000c000d097e0:  00007fddb3e17bc9 <runtime.cgocall+137>  00007fddb43c0070
000000c000d097f0:  000000c000d09850  000000c000000000
000000c000d09800:  0000000000d09830 <github.com/microcosm-cc/bluemonday.UGCPolicy+2448>  0000000001ee2620
000000c000d098d0:  000000c001801f88  00007fddac071698
000000c000d098e0:  000000c001801f00  0000000000000000
000000c000d098f0:  0000000000000000  0000000000000001
000000c000d09900:  0000000000000000  0000000000000000
000000c000d09910:  00007fddac350c28  000000c001801f00
000000c000d09920:  0000000000000005  0000000000000000
000000c000d09930:  0000000000000000  00007fddac350c28
000000c000d09940:  0000000000000001  0000000000000000
000000c000d09950:  0000000000000000
fatal error: unknown caller pc
runtime stack:
runtime.throw(0x12ebd52, 0x11)
/usr/local/go/src/runtime/panic.go:1116 +0x74
runtime.gentraceback(0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0xc0012ead80, 0x0, 0x0, 0x7fffffff, 0x7fdd5e7fbbb8, 0x0, 0x0, …)
/usr/local/go/src/runtime/traceback.go:273 +0x1aac
runtime.scanstack(0xc0012ead80, 0xc000547e98)
/usr/local/go/src/runtime/mgcmark.go:844 +0x165
runtime.markroot.func1()
/usr/local/go/src/runtime/mgcmark.go:245 +0xc6
runtime.markroot(0xc000547e98, 0xea)
/usr/local/go/src/runtime/mgcmark.go:218 +0x345
runtime.gcDrain(0xc000547e98, 0x3)
/usr/local/go/src/runtime/mgcmark.go:1108 +0x146
runtime.gcBgMarkWorker.func2()
/usr/local/go/src/runtime/mgc.go:1958 +0x87
runtime.systemstack(0x1e7e050)
/usr/local/go/src/runtime/asm_amd64.s:370 +0x63
runtime.mstart()
/usr/local/go/src/runtime/proc.go:1116
goroutine 25 [GC worker (idle)]:
runtime.systemstack_switch()
/usr/local/go/src/runtime/asm_amd64.s:330 fp=0xc000186f60 sp=0xc000186f58 pc=0x8d1be0
runtime.gcBgMarkWorker(0xc000546800)
/usr/local/go/src/runtime/mgc.go:1945 +0x1ea fp=0xc000186fd8 sp=0xc000186f60 pc=0x87f9ca
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000186fe0 sp=0xc000186fd8 pc=0x8d39c1
created by runtime.gcBgMarkStartWorkers
/usr/local/go/src/runtime/mgc.go:1839 +0x85
goroutine 1 [IO wait]:
internal/poll.runtime_pollWait(0x7fddac3c5798, 0x72, 0x0)
/usr/local/go/src/runtime/netpoll.go:220 +0x65
internal/poll.(*pollDesc).wait(0xc00081e798, 0x72, 0x0, 0x0, 0x12dc0f0)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:87 +0x47
internal/poll.(*pollDesc).waitRead(…)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Accept(0xc00081e780, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
/usr/local/go/src/internal/poll/fd_unix.go:394 +0x1fc
net.(*netFD).accept(0xc00081e780, 0x7667f3ba6dfc7c5a, 0x0, 0x0)
/usr/local/go/src/net/fd_unix.go:172 +0x45
net.(*TCPListener).accept(0xc00086f340, 0x60e5b630, 0xc0002f7c58, 0x8faa28)
/usr/local/go/src/net/tcpsock_posix.go:139 +0x34
net.(*TCPListener).Accept(0xc00086f340, 0xc0002f7ca8, 0x18, 0xc000000180, 0xb7d954)
/usr/local/go/src/net/tcpsock.go:261 +0x66
net/http.(*Server).Serve(0xc0002e8000, 0x192dc00, 0xc00086f340, 0x0, 0x0)
/usr/local/go/src/net/http/server.go:2937 +0x286
net/http.(*Server).ListenAndServe(0xc0002e8000, 0xc0002e8000, 0xc00086f320)
/usr/local/go/src/net/http/server.go:2866 +0xb9
net/http.ListenAndServe(…)
/usr/local/go/src/net/http/server.go:3120
main.main()
goroutine 22 [select, 50 minutes]:
database/sql.(*DB).connectionOpener(0xc0001bfad0, 0x1932fc0, 0xc0005a7b80)
/usr/local/go/src/database/sql/sql.go:1126 +0xe8
created by database/sql.OpenDB
/usr/local/go/src/database/sql/sql.go:740 +0x13f
goroutine 11340 [IO wait]:
internal/poll.runtime_pollWait(0x7fddac3c4d18, 0x72, 0x191f800)
/usr/local/go/src/runtime/netpoll.go:220 +0x65
internal/poll.(*pollDesc).wait(0xc0007cc418, 0x72, 0xc000637d00, 0x1, 0x1)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:87 +0x47
internal/poll.(*pollDesc).waitRead(…)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Read(0xc0007cc400, 0xc000637db1, 0x1, 0x1, 0x0, 0x0, 0x0)
/usr/local/go/src/internal/poll/fd_unix.go:159 +0x1b1
net.(*netFD).Read(0xc0007cc400, 0xc000637db1, 0x1, 0x1, 0xbf5152, 0xc0001b4ae0, 0xc00070af80)
/usr/local/go/src/net/fd_posix.go:55 +0x51
net.(*conn).Read(0xc000d14018, 0xc000637db1, 0x1, 0x1, 0x0, 0x0, 0x0)
/usr/local/go/src/net/net.go:182 +0x90
net/http.(*connReader).backgroundRead(0xc000637da0)
/usr/local/go/src/net/http/server.go:690 +0x5a
created by net/http.(*connReader).startBackgroundRead
/usr/local/go/src/net/http/server.go:686 +0xd5
goroutine 10891 [select]:
github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1(0xc000c16360, 0xc0007e45a0, 0xc0002a84e0)
/root/go/pkg/mod/github.com/go-sql-driver/mysql@v1.5.0/connection.go:621 +0xbf
created by github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher
/root/go/pkg/mod/github.com/go-sql-driver/mysql@v1.5.0/connection.go:618 +0xc8
goroutine 11381 [IO wait]:
internal/poll.runtime_pollWait(0x7fddac3c5178, 0x72, 0x191f800)
/usr/local/go/src/runtime/netpoll.go:220 +0x65
internal/poll.(*pollDesc).wait(0xc0007cc998, 0x72, 0xc00092e000, 0x1000, 0x1000)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:87 +0x47
internal/poll.(*pollDesc).waitRead(…)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Read(0xc0007cc980, 0xc00092e000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/usr/local/go/src/internal/poll/fd_unix.go:159 +0x1b1
net.(*netFD).Read(0xc0007cc980, 0xc00092e000, 0x1000, 0x1000, 0xc000e02f00, 0x300000002, 0xc000e02f00)
/usr/local/go/src/net/fd_posix.go:55 +0x51
net.(*conn).Read(0xc000d14048, 0xc00092e000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
/usr/local/go/src/net/net.go:182 +0x90
net/http.(*connReader).Read(0xc000897d10, 0xc00092e000, 0x1000, 0x1000, 0x8, 0x0, 0x0)
/usr/local/go/src/net/http/server.go:798 +0x1c2
bufio.(*Reader).fill(0xc000903ec0)
/usr/local/go/src/bufio/bufio.go:101 +0x105
bufio.(*Reader).ReadSlice(0xc000903ec0, 0xa, 0x1481128, 0xc001423988, 0x86e990, 0xc000d48a00, 0x100)
/usr/local/go/src/bufio/bufio.go:360 +0x3f
bufio.(*Reader).ReadLine(0xc000903ec0, 0xc001423990, 0xc0006d0000, 0x7fddacfd9c28, 0x0, 0x86f37a, 0x30)
/usr/local/go/src/bufio/bufio.go:389 +0x36
net/textproto.(*Reader).readLineSlice(0xc000c15ad0, 0xc000d48a00, 0x94404f, 0xc0007cc980, 0x0, 0xc0007cc980)
/usr/local/go/src/net/textproto/reader.go:58 +0x6e
net/textproto.(*Reader).ReadLine(…)
/usr/local/go/src/net/textproto/reader.go:39
net/http.readRequest(0xc000903ec0, 0x0, 0xc000d48a00, 0x0, 0x0)
/usr/local/go/src/net/http/request.go:1012 +0xac
net/http.(*conn).readRequest(0xc0003b94a0, 0x1932fc0, 0xc000082100, 0x0, 0x0, 0x0)
/usr/local/go/src/net/http/server.go:984 +0x19a
net/http.(*conn).serve(0xc0003b94a0, 0x1932fc0, 0xc000082100)
/usr/local/go/src/net/http/server.go:1851 +0x70d
created by net/http.(*Server).Serve
/usr/local/go/src/net/http/server.go:2969 +0x394

更多关于Golang中如何解决goroutine运行时错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

谢谢

更多关于Golang中如何解决goroutine运行时错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


抱歉,是的,我会回复你的。

你能提供重现它的步骤吗?

你好 @skillian 能否请你回复我之前提出的问题?

崩溃并非每次都会发生,它非常罕见。没有重现崩溃的步骤。我正在调用一个插件方法。崩溃是随机发生的。下面这行信息每次都会出现:

/usr/local/go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000186fe0 sp=0xc000186fd8 pc=0x8d39c1
created by runtime.gcBgMarkStartWorkers

ManojKumarChauhan:

我可以从Go函数中同时调用多个cgo函数(多线程)吗?

可以。

ManojKumarChauhan:

同样地,我可以从cgo函数中同时调用Go函数吗?

这很有趣;你的代码是在整个调用栈中在C和Go之间来回切换吗?这可能会有问题,因为Go的栈会增长并且可以被移动。我不确定如果其中混入了一些C栈会发生什么。我甚至不知道C栈和Go栈是否是同一个栈(我对cgo几乎没有任何经验)。

我理解在不复现问题的情况下很难找到原因。我对Go语言并不精通,所以想请教一些常见问题。我认为我可能没有遵循Go语言的基本规则。

我创建了一个插件并编写了一些Go函数。在插件中还使用了一个C语言库,从Go函数中调用cgo函数,并且在插件中编写了一些cgo函数。

我可以从Go函数中同时调用多个cgo函数(多线程)吗? 同样地,我可以从cgo函数中同时调用Go函数吗?

在大多数情况下,我在runtime.cgocall和proc.go:1116(mstart())以及runtime.systemstack_switch()中遇到错误。

这个错误通常发生在使用CGO调用插件时,栈帧信息不一致导致的。主要原因是插件中的函数指针与Go运行时的调用约定不匹配。

以下是几种可能的解决方案:

1. 使用runtime.KeepAlive确保CGO对象不被回收

import "runtime"

func callPlugin() {
    // 调用插件函数
    result := pluginFunc()
    
    // 确保插件相关的资源在函数返回前不被GC回收
    runtime.KeepAlive(pluginHandle)
}

2. 检查插件编译选项是否匹配 确保插件和主程序使用相同的编译标志:

// 构建插件时
// go build -buildmode=plugin -o plugin.so plugin.go

// 主程序加载插件
import "plugin"

func main() {
    p, err := plugin.Open("plugin.so")
    if err != nil {
        panic(err)
    }
    
    sym, err := p.Lookup("Testmethod")
    if err != nil {
        panic(err)
    }
    
    testFunc, ok := sym.(func() error)
    if !ok {
        panic("unexpected type from module symbol")
    }
    
    err = testFunc()
    if err != nil {
        panic(err)
    }
}

3. 使用sync.Pool管理CGO调用

import (
    "sync"
    "unsafe"
)

var cgoPool = sync.Pool{
    New: func() interface{} {
        // 初始化CGO资源
        return initCGO()
    },
}

func safeCall() {
    cgoObj := cgoPool.Get().(unsafe.Pointer)
    defer cgoPool.Put(cgoObj)
    
    // 使用cgoObj调用插件函数
    callWithCGO(cgoObj)
}

4. 设置GODEBUG环境变量调试

// 在程序启动前设置
func init() {
    os.Setenv("GODEBUG", "cgocheck=0")
}

// 或者运行时设置
func main() {
    debug.SetTraceback("system")
    // ... 程序逻辑
}

5. 使用runtime.LockOSThread绑定线程

func callPluginSafely() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()
    
    // 调用插件函数
    pluginFunc()
}

6. 检查内存对齐问题

import "unsafe"

// 确保C结构体与Go结构体对齐
/*
#include <stddef.h>
typedef struct {
    int64_t field1;
    void*   field2;
} CStruct;
*/
import "C"

type GoStruct struct {
    Field1 int64
    Field2 unsafe.Pointer
}

// 验证大小和对齐
var _ = [unsafe.Sizeof(GoStruct{})]byte{}
var _ = [C.sizeof_CStruct]byte{}

7. 使用cgo.Handle进行安全的回调

/*
#include <stdint.h>
extern void goCallback(uintptr_t handle);
static void callGo(uintptr_t handle) {
    goCallback(handle);
}
*/
import "C"
import "runtime/cgo"

func setupCallback() {
    h := cgo.NewHandle(func() {
        // 回调逻辑
    })
    
    C.callGo(C.uintptr_t(h))
}

8. 确保插件接口定义正确

// 插件接口定义
type Plugin interface {
    Testmethod() error
    Cleanup()
}

// 安全调用模式
func safePluginCall(p Plugin) error {
    defer func() {
        if r := recover(); r != nil {
            // 处理panic
        }
    }()
    
    err := p.Testmethod()
    p.Cleanup()
    return err
}

这个错误通常与CGO调用约定、内存管理或GC相关。需要确保插件函数遵循Go的调用约定,并且在CGO调用期间保持相关对象的存活。

回到顶部