Golang中共享库的Goroutine无法启动问题
Golang中共享库的Goroutine无法启动问题 我正在尝试创建一个用于SSH授权的PAM模块。
不幸的是,在SSH流程中使用时,每个goroutine在PAM模块内都无法启动。而使用pamtester时,它确实可以正常工作,没有任何问题。
以下是代码:
package main
/*
#include <security/pam_modules.h>
typedef const char cchar_t;
*/
import "C"
import (
"log"
"time"
)
func main() {}
//export pam_sm_authenticate
func pam_sm_authenticate(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int {
log.Printf("golib.so: before start goroutine")
go func() {
// This will not happen and the whole program will hang from
// here on...
log.Printf("golib.so: something out of the goroutine")
}()
log.Printf("golib.so: after start goroutine")
return C.PAM_SUCCESS
}
//export pam_sm_setcred
func pam_sm_setcred(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int {
return C.PAM_IGNORE
}
环境
- OS: Ubuntu 22.04.4 LTS
- SSH: OpenSSH_8.9p1 Ubuntu-3ubuntu0.10, OpenSSL 3.0.2 15 Mar 2022
- Go: go version go1.22.5 linux/amd64
- pamtester: 0.1.2
类似问题
- golang/go#57394
- golang/go#15538
- golang/go#15556
到目前为止,这些工单都没有帮助我解决问题。
我的主要问题甚至不在于我打算使用goroutine,而是如果我使用http.Client,goroutine无处不在。
顺便说一句:不要过多关注方法内部发生了什么。goroutine甚至没有启动。甚至连golib.so: goroutine begin都不会显示。
所以:我很乐意接受建议,让goroutine在SSH内的PAM模块上下文中工作,或者在不使用goroutine的情况下进行HTTP请求。
感谢您的支持!
更多关于Golang中共享库的Goroutine无法启动问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好,可以尝试以下方法:
- 使用同步的 HTTP 调用,而不使用 goroutine。
- 创建一个自定义的
http.Transport来禁用 goroutine。 - 检查 SSH/PAM 日志以排查线程问题。
- 使用
net.Dial进行手动的 HTTP 请求。 - 使用
runtime.GOMAXPROCS(1)来限制 Go 线程数。
希望这些方法对你有用……
更多关于Golang中共享库的Goroutine无法启动问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个典型的Go共享库在C环境中运行时的问题。问题的核心在于Go的运行时初始化与PAM模块加载机制不兼容。当SSH加载PAM模块时,Go的运行时没有正确初始化,导致goroutine无法启动。
以下是解决方案:
package main
/*
#include <security/pam_modules.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
typedef const char cchar_t;
*/
import "C"
import (
"log"
"os"
"runtime"
"syscall"
"unsafe"
)
func main() {}
// 初始化Go运行时
func init() {
// 确保Go运行时正确初始化
runtime.LockOSThread()
}
//export pam_sm_authenticate
func pam_sm_authenticate(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int {
log.Printf("golib.so: before start goroutine")
// 使用fork+exec模式来启动goroutine
pid, err := syscall.ForkExec("/proc/self/exe", []string{"goroutine_worker"}, &syscall.ProcAttr{
Env: os.Environ(),
Files: []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)},
})
if err != nil {
log.Printf("golib.so: fork failed: %v", err)
return C.PAM_SUCCESS
}
// 非阻塞等待
go func() {
var status syscall.WaitStatus
syscall.Wait4(pid, &status, 0, nil)
log.Printf("golib.so: goroutine worker exited")
}()
log.Printf("golib.so: after start goroutine")
return C.PAM_SUCCESS
}
// 替代方案:使用C线程启动goroutine
//export start_goroutine_via_c_thread
func start_goroutine_via_c_thread() {
// 通过CGO调用pthread_create来创建线程
C.pthread_create(nil, nil, (*[0]byte)(C.start_go_routine), nil)
}
//export go_routine_entry
func go_routine_entry() {
log.Printf("golib.so: goroutine started via C thread")
// 这里可以执行你的HTTP请求
}
// 如果必须使用HTTP客户端,使用同步方式
func makeHTTPRequestSync() {
// 使用net/http包但避免goroutine
// 注意:这仍然可能内部使用goroutine,所以最好使用fork模式
}
//export pam_sm_setcred
func pam_sm_setcred(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int {
return C.PAM_IGNORE
}
需要添加的C代码:
#include <pthread.h>
extern void go_routine_entry();
void* start_go_routine(void* arg) {
go_routine_entry();
return NULL;
}
编译命令:
# 使用CGO_ENABLED=1和-buildmode=c-shared
go build -buildmode=c-shared -o pam_golang.so ./pam_module.go
关键点:
- Go共享库在PAM环境中运行时,运行时初始化不完整
- 使用
fork+exec模式是绕过此限制的最可靠方法 - 或者通过C线程(
pthread)间接启动goroutine - 避免在PAM模块中直接使用可能创建goroutine的Go库(如net/http)
对于HTTP请求,建议使用外部进程或守护进程来处理,PAM模块只进行认证决策。

