谨慎在多进程中使用Go生成的动态库

谨慎在多进程中使用Go生成的动态库,第1张

之前写过一篇《C语言调用Go生成的动态库中的函数》,实际在OpenSIPS中使用时,却发现程序经常会“陷入”动态库函数的调用。

使用GDB附加到进程,函数调用block时堆栈如下:

#0  runtime.futex () at /usr/local/go/src/runtime/sys_linux_amd64.s:520
#1  0x00007f13340e7d16 in runtime.futexsleep (addr=0xfffffffffffffe00, val=0, ns=-1) at /usr/local/go/src/runtime/os_linux.go:44
#2  0x00007f13340c5367 in runtime.notesleep (n=0xfffffffffffffe00) at /usr/local/go/src/runtime/lock_futex.go:160
#3  0x00007f13340f10ea in runtime.mPark () at /usr/local/go/src/runtime/proc.go:1439
#4  0x00007f13340f2d65 in runtime.stoplockedm () at /usr/local/go/src/runtime/proc.go:2602
#5  0x00007f13340f483d in runtime.schedule () at /usr/local/go/src/runtime/proc.go:3299
#6  0x00007f13340f4f8d in runtime.park_m (gp=0xc000000680) at /usr/local/go/src/runtime/proc.go:3516
#7  0x00007f1334115ec5 in runtime.mcall () at /usr/local/go/src/runtime/asm_amd64.s:307
#8  0x00007ffc3c0d4398 in ?? ()
#9  0x00007ffc3c0d43b0 in ?? ()
#10 0x0000000000000000 in ?? ()

看起来像是发生死锁了。为什么单元测试中表现正常的Go动态库,在OpenSIPS调用时却发生死锁了呢?

第一时间就怀疑和OpenSIPS的多进程架构有关,打开google搜索关键字:"go library"、"fork"、"process"。很好,立马找到github上三个golang的issue:

Golang Shared library behavior in forked processes · Issue #16855 · golang/go · GitHub

Goroutines cause deadlocks after `fork()` when run in shared library · Issue #15538 · golang/go · GitHub

https://github.com/golang/go/issues/15556

总结一下:

1. 首先,在多线程中使用fork是一件很危险的事情,Linux Man Page中对fork的说明:

The child process is created with a single thread--the one that called fork(). The entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects; the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause.

fork的时候只复制当前线程到子进程。假设在fork之前,一个线程对某个互斥锁进行lock *** 作,即持有了该锁,然后另外一个线程调用了fork创建子进程。可是在子进程中持有那个锁的线程却”消失”了,从子进程的角度来看,这个锁被“永久”的上锁了,因为它的持有者“蒸发”了。这是导致死锁问题的根源。

2. Go的动态库是隐式加载的,也就是说单线程的OpenSIPS加载动态库后由于Go的runtime变成了多线程,随后fork出各个工作进程,导致了在业务调用时runtime的死锁问题。

我采用的解决方法是这样的:在业务实际调用到动态库时,再显式加载动态库。对于我的场景,是在HTTPD这个工作进程中加载Go动态库。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/994936.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存