为什么 exec.Command.Start() 会挂在 darwin 上?

运行某个程序的特定开发版本时,我偶尔会遇到挂起,而使用官方版本似乎不会挂起。开发版本的不同之处主要在于它引入了更多的 Go std 库,(大部分)它没有使用;所以可执行文件更大,加上 static-var 和 init() 初始化已完成,这可能会增加遇到某些竞争条件的可能性。


git bisect run将(golang)的罪魁祸首确定为6becb033341602f2df9d7c55cc23e64b925bbee2:


Author: Ian Lance Taylor <iant@golang.org>

Date:   Thu Apr 11 16:53:11 2019 -0700


[...]


    runtime: switch to using new timer code


diff --git a/src/runtime/time.go b/src/runtime/time.go

index fea5d6871c..db48a932d4 100644

--- a/src/runtime/time.go

+++ b/src/runtime/time.go

@@ -14,7 +14,7 @@ import (

 )


 // Temporary scaffolding while the new timer code is added.

-const oldTimers = true

+const oldTimers = false


 // Package time knows the layout of this structure.

 // If this struct changes, adjust ../time/sleep.go:/runtimeTimer.

浏览了这个小改动带来的差异后,我强烈倾向于在这个“新计时器代码”和/或它启用的代码中存在一些竞争条件。


无论是通过 Ctrl-\ ( SIGQUIT ) 还是delve attach,罪魁祸首似乎总是cmd.Start()这里的调用:


func sh(dir string, stdin io.Reader, stdout io.Writer, stderr io.Writer, name string, args []string) Object {

cmd := exec.Command(name, args...)

cmd.Dir = dir

cmd.Stdin = stdin


var stdoutBuffer, stderrBuffer bytes.Buffer

if stdout != nil {

    cmd.Stdout = stdout

} else {

    cmd.Stdout = &stdoutBuffer

}

if stderr != nil {

    cmd.Stderr = stderr

} else {

    cmd.Stderr = &stderrBuffer

}


err := cmd.Start()

PanicOnErr(err)

从那里开始的堆栈跟踪看起来非常相似,直到到达syscall/exec_unix.go(在 Go 源代码树中)。然后,在 Delve 中,似乎挂起的是forkAndExecInChild()调用,而 Ctrl-\ 将readlen()调用显示为挂起:


// Kick off child.

pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])

if err1 != 0 {

    err = Errno(err1)

    goto error

}

ForkLock.Unlock()


// Read child error status from pipe.

Close(p[1])

n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))

Close(p[0])

if err != nil || n != 0 {

forkAndExecInChild()代码似乎挂在,这是一个循环内exec_darwin.go:206的系统调用。libc_dup2_trampoline假设这只是对 的调用dup2(),我想不出它会挂起的任何原因;但是我已经“捕获”了一个挂起的测试运行在那里(并且没有其他地方)至少两次, via delve,尽管这可能只是使用delve attach <pid> ...与 Ctrl-\ (SIGQUIT)的产物?



明月笑刀无情
浏览 133回答 1
1回答

DIEA

这是 Go for MacOS 上的一个错误,已在https://go-review.googlesource.com/c/go/+/372798/中得到修复对于修复之前受影响的 Go 版本,解决方法是传递-Wl,-bind_at_load给链接器,这可以通过调用 go with-ldflags="-extldflags=-Wl,-bind_at_load"
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go