Golang中Cgo与net/http在MacOS运行时出现段错误问题
Golang中Cgo与net/http在MacOS运行时出现段错误问题
func main() {
fmt.Println("hello world")
}
使用 Go 1.21.1,并尝试用 cgo 包装一个原生库,详见 GenomicsDB-Go。该原生库外部依赖 curl/openssl/zlib。绑定库在 Linux 和 MacOS 平台上都能正常构建和测试,但当同时导入 net/http 和 github.com/GenomicsDB/GenomicsDB-Go/bindings 时,仅在 MacOS 上调用系统调用 connect() 时会发生段错误。该原生库确实依赖 OSX 框架进行系统调用,不知何故这与 Go 运行时发生了冲突。对此有什么建议吗?谢谢!
更多关于Golang中Cgo与net/http在MacOS运行时出现段错误问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
移除 gdb 导入和 fmt.Println,仅保留 http.Get 的结果会怎样?
更多关于Golang中Cgo与net/http在MacOS运行时出现段错误问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
github.com/datma-health/federated-fiber/common/auth.getJSON
这段代码是做什么的?
github.com/datma-health/federated-fiber/common/auth.getJSON
无论这个函数在做什么,它都在发起 HTTP 调用,而正是这个调用导致了段错误,我不确定这是否是你的库的问题。我建议在你的代码中设置一些断点,然后从这个函数开始逐步调试。
注释掉这些部分后,代码运行正常!
~/GenomicsDB-Go/example-genomicsdb-go: !vi
vi main.go
~/GenomicsDB-Go/example-genomicsdb-go: go build
~/GenomicsDB-Go/example-genomicsdb-go: go run .
~/GenomicsDB-Go/example-genomicsdb-go: echo $?
0
实际上,我仅用这段代码就重现了该问题。我已经用上面的调用栈更新了整个段错误信息。
package main
import (
"log"
"net/http"
gdb "github.com/GenomicsDB/GenomicsDB-Go/bindings"
)
func main() {
_, err1 := http.Get("http://www.google.com/robots.txt")
if err1 != nil {
log.Fatal(err1)
}
log.Println("Got GenomicsDB version: ", gdb.GetVersion())
}
感谢 gabereiser。我正在同一台机器上构建原生的 GenomicsDB 库及其绑定,目前尚未进行交叉编译——系统是 macOS 13.x 和 arm64 架构,所有这些看起来都是同步的。
go env | grep ARCH
GOARCH='arm64'
GOHOSTARCH='arm64'
以及
lipo -info /usr/local/lib/libtiledbgenomicsdb.dylib
Non-fat file: /usr/local/lib/libtiledbgenomicsdb.dylib is architecture: arm64
genomicsdb 是作为 install-bindings 的一部分构建的,不幸的是这是一个单独的步骤。你可以执行 go install github.com/GenomicsDB/GenomicsDB-Go/install-genomicsdb@latest,然后调用 install-genomicsdb。这应该会将 genomicsdb 安装到 /usr/local/lib 和 /usr/local/include 目录下,并将 genomicsdb.pc 文件放在 /usr/local/lib/pkgconfig 中,以便 cgo 能够发现。如果需要,你可以添加环境变量 PKG_CONFIG_PATH=/usr/local/lib/pkgconfig。
func main() {
fmt.Println("hello world")
}
第一反应是检查GenomicsDB的二进制文件,确认它是否针对aarch64架构构建,因为您使用的是Apple Silicon。在您仓库的install.sh文件中,我没有看到您是否设置了架构参数。它只检查了操作系统。同样,在GenomicsDB的CMakeLists中,我也没有看到任何针对arm的检查或标志,这让我推测它是针对amd64/x86架构的。请使用GOARCH=amd64构建您的Mac库,看看是否仍然发生段错误。如果没有,您需要为Apple Silicon构建aarch64架构的GenomicsDB,以便在Apple Silicon上的Go中使用它。
使用您最新的简化代码,由于在pkg-config中找不到genomicsdb,导致编译失败。通常Go绑定会使用cgo来构建包。请修复此问题,以便当我调用go build并构建您的绑定时,genomicsdb也能被构建。您已经完成了一半的工作,但如果Go绑定无法编译,我将无法进一步调试或提供帮助。
Build Error: go build -o test -gcflags all=-N -l .
go build github.com/GenomicsDB/GenomicsDB-Go/bindings:
# pkg-config --cflags -- genomicsdb
Package genomicsdb was not found in the pkg-config search path.
Perhaps you should add the directory containing "genomicsdb.pc"
to the PKG_CONFIG_PATH environment variable
No package ‘genomicsdb’ found
pkg-config: exit status 1 (exit status 1)
这是一个典型的Cgo与Go运行时在macOS上的兼容性问题。问题很可能出现在C库的初始化与Go的net/http包之间的冲突上。
根据你的描述,原生库依赖OSX框架进行系统调用,这可能会与Go运行时的网络栈产生冲突。以下是几个关键点和解决方案:
1. 初始化顺序问题
C库的初始化可能需要在Go运行时初始化之前完成。尝试在init()函数中显式初始化C库:
package main
import (
"fmt"
"net/http"
_ "github.com/GenomicsDB/GenomicsDB-Go/bindings"
)
/*
#cgo LDFLAGS: -lcurl -lssl -lcrypto -lz
#include <stdlib.h>
// 假设这是你的C库初始化函数
void init_genomicsdb();
*/
import "C"
func init() {
// 在Go运行时初始化之前调用C库初始化
C.init_genomicsdb()
}
func main() {
fmt.Println("hello world")
// 现在可以安全使用net/http
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
})
http.ListenAndServe(":8080", nil)
}
2. 线程局部存储(TLS)冲突
macOS上的某些C库(特别是OpenSSL)可能与Go的线程模型冲突。尝试设置环境变量:
package main
import (
"fmt"
"net/http"
"os"
_ "github.com/GenomicsDB/GenomicsDB-Go/bindings"
)
func init() {
// 防止OpenSSL与Go运行时冲突
os.Setenv("GODEBUG", "asyncpreemptoff=1")
os.Setenv("CGO_CFLAGS", "-DOPENSSL_NO_ASYNC")
}
func main() {
fmt.Println("hello world")
client := &http.Client{}
resp, err := client.Get("http://example.com")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer resp.Body.Close()
fmt.Println("Request successful")
}
3. 信号处理冲突
C库可能设置了信号处理器,与Go运行时冲突。在导入Cgo绑定前设置信号处理:
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
)
/*
#cgo LDFLAGS: -lcurl -lssl -lcrypto -lz
#include <signal.h>
#include <stdlib.h>
static void setup_signals() {
// 忽略可能冲突的信号
signal(SIGPIPE, SIG_IGN);
}
*/
import "C"
func init() {
// 设置C信号处理
C.setup_signals()
// 设置Go信号处理
signal.Notify(make(chan os.Signal, 1), syscall.SIGPIPE)
}
// 延迟导入绑定库
import _ "github.com/GenomicsDB/GenomicsDB-Go/bindings"
func main() {
fmt.Println("hello world")
// 测试HTTP请求
req, err := http.NewRequest("GET", "http://example.com", nil)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("Error making request: %v\n", err)
return
}
defer resp.Body.Close()
fmt.Printf("Status: %s\n", resp.Status)
}
4. 使用runtime.LockOSThread()
如果问题与线程相关,可以尝试锁定OS线程:
package main
import (
"fmt"
"net/http"
"runtime"
_ "github.com/GenomicsDB/GenomicsDB-Go/bindings"
)
func init() {
// 锁定初始化到主线程
runtime.LockOSThread()
}
func main() {
defer runtime.UnlockOSThread()
fmt.Println("hello world")
// 创建HTTP服务器
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from GenomicsDB"))
}),
}
fmt.Println("Starting server on :8080")
if err := server.ListenAndServe(); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}
5. 编译时标志
尝试添加特定的编译标志:
# 构建时添加这些标志
export CGO_CFLAGS="-D_DARWIN_C_SOURCE -mmacosx-version-min=10.14"
export CGO_LDFLAGS="-framework CoreFoundation -framework Security"
go build -ldflags="-s -w -linkmode=external" main.go
或者在代码中指定:
// #cgo CFLAGS: -D_DARWIN_C_SOURCE -mmacosx-version-min=10.14
// #cgo LDFLAGS: -framework CoreFoundation -framework Security -lcurl -lssl -lcrypto -lz
import "C"
调试建议
如果上述方法都不奏效,可以添加调试代码来定位具体崩溃位置:
package main
import (
"fmt"
"net/http"
"runtime/debug"
)
func main() {
// 设置崩溃恢复
defer func() {
if r := recover(); r != nil {
fmt.Printf("Panic recovered: %v\n", r)
debug.PrintStack()
}
}()
fmt.Println("hello world")
// 分段测试
fmt.Println("Testing HTTP client creation...")
client := http.DefaultClient
fmt.Println("Testing HTTP request...")
resp, err := client.Get("http://localhost:8080/test")
if err != nil {
fmt.Printf("Expected error (no server): %v\n", err)
} else {
resp.Body.Close()
}
fmt.Println("All tests passed")
}
最可能的原因是C库的线程局部存储或信号处理与Go运行时的网络栈冲突。建议先从初始化顺序和信号处理入手,逐步测试每个可能的冲突点。

