提供一个非常简单的示例(我仍然不确定在任何ENV上都可以完全重现) 所以有一个插座管
func SocketPair() (*os.File, *os.File, error) {
fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
if err != nil {
return nil, nil, err
}
f0 := os.NewFile(uintptr(fds[0]), "socket-0")
f1 := os.NewFile(uintptr(fds[1]), "socket-1")
return f0, f1, nil
}
和简单的CMD调用
func main() {
f0, f1, err := utils.SocketPair()
if err != nil {
return panic(err)
}
cmd := exec.CommandContext(ctx, "cat")
cmd.Stdin = f0
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// pipe routine
go func() {
size, err := io.Copy(f1, os.Stdin)
fmt.Printf("------res %d, %v", size, err)
f1.Close()
}()
err := cmd.Run()
if err != nil {
return panic(err)
}
}
so用类似的东西打电话给这个
echo "abc\ndef\nghi" | app
采用类似于
------res 11, <nil>
abc
def
ghi
悬挂。实际上,它说
pipe routine
成功地将STDIN数据传递到了插座对。然而,CMD输入仍然没有EOF。
对于这个确切的简单示例,该问题(在我的env中)可以通过两个选项解决。put put
pipe routine
在
cmd := exec.
pipe routine
因此,这两个解决方案都解决了问题,并且应用程序优雅地退出。
started := make(chan byte)
go func() {
close(started)
size, err := io.Copy(f1, os.Stdin)
fmt.Printf("------res %d, %v", size, err)
f1.Close()
}()
<-started
在工作之前就在
time.Sleep(time.Second)
cmd.Run()
在这里搜索最佳间隔(如果这确实是一个竞赛条件,这是一个坏主意)我的关键问题在这里:这种行为的根本原因是什么。谁首先开始阅读到底有什么问题。 thanks
我猜想,您看到的悬挂问题是在设置管道的读数和写作何时设置的情况之间。这很棘手,因为有时它可以工作,有时不行! 在这里,您的两个解决方案都可以使用:
先前介绍管道例程:这使Goroutine有时间在
io.Copy / cmd.Run
尝试阅读之前启动。
使用通道同步:
启动:= make(chan byte) go func(){ 关闭(开始) //您的复制代码 }()
这确保您的goroutine实际上在继续前进。 而不是使用
time.Sleep
,例如:
cmd.Run()
根问题是,我们需要确保在命令开始尝试从中读取该管道之前准备处理管道。