go os/exec: 在Windows上,不再可能将所有可继承的文件句柄传递给子进程,

kpbwa7wx  于 3个月前  发布在  Go
关注(0)|答案(8)|浏览(49)

你正在使用的Go版本是什么( go version )? 1.17.10

这个问题在最新版本中是否重现?没有测试,但最近没有与此相关的更改

你正在使用什么操作系统和处理器架构( go env )? Windows amd64

你做了什么?

#48393 @alexbrainman
This commit (@zx2c4 )更改了Windows上的进程创建,使得只有stdin/stdout/stderr以及AdditionalInheritedHandles中列出的句柄可以被子进程继承。
不幸的是,这与我们使用的Python wrapper tool 交互得不好。这个 Package 器设置了一个包含所有必要软件包的虚拟环境,然后简单地向Python解释器发出命令。
一些调用者依赖于能够将文件句柄(即管道)传递给Python脚本。如果直接调用Python,这是可以正常工作的,但是由于对golang实现的这种更改,我们的 Package 器不再将这些句柄传递给Python子进程。
由于 Package 器不了解调用者希望传递的文件句柄,我们不能只是将它们添加到AdditionalInheritedHandles中。

你期望看到什么?

如果能以某种方式选择回到旧的行为,那就太好了,可以使用os/exec。

你看到了什么?

似乎没有办法回到旧的行为,除了在我们自己的代码中进行所有低级Windows调用之外。
谢谢,

pobjuy32

pobjuy321#

@brianryner8 感谢您创建这个问题。
您能否提供一个在1.17.10之前的小可运行示例,但现在不起作用?
谢谢。
Alex

9jyewag0

9jyewag02#

#48393中的示例展示了这个问题。对我们来说,唯一的区别是,而不是在我们的Go应用程序中创建管道,当我们的Go二进制文件被执行时,它作为可继承的句柄传递。

bq3bfh9z

bq3bfh9z3#

#48393中的示例展示了这个问题。
我不认为这段代码有问题。这段代码只需要使用AdditionalInheritedHandles。Go 1.17添加了AdditionalInheritedHandles,现在如果你想在进程之间传递句柄,每个人都必须使用它。
Alex

u2nhd7ah

u2nhd7ah4#

完全抛开这个变化破坏了 go1compat (在我看来,这本身就足以成为改变默认行为回退到先前行为的理由),在这种情况下的问题是,这里的 Go 程序 不知道 应该传播哪些句柄,因此没有简单的方法来设置 AdditionalInheritedHandles。
流程如下:

  • 父进程(我们无法控制)将一些句柄标记为可继承的,并调用带有某些 Python 脚本的 "python"(我们也无法控制)。
  • 我们使用的 Go 二进制文件(在 Windows 上作为 "python")进行一些设置(创建虚拟环境),然后执行(或尽可能接近 Windows 上的执行)进入实际的 Python 解释器。
  • Python 解释器运行,运行用户控制的 Python 脚本,该脚本期望句柄按照父进程设置的方式设置。

在 Go 1.16 中,这工作得很好。在 Go 1.17 中,我们不得不复制 exec_windows.go(https://chromium-review.googlesource.com/c/infra/luci/luci-go/+/3739699) 的很大一部分。
提供一个函数可能是合理的妥协,让程序可以调用该函数,用旧版本中存在的任何句柄填充 AdditionalInheritedHandles(尽管,再次强调,这违背了 go1compat...但至少它不会让用户陷入困境)。

u4dcyp6a

u4dcyp6a5#

感谢您的评论。
完全抛开这个改变破坏了 go1compat 的问题,您的程序假设 syscall.StartProcess 会将所有可继承的句柄传递给新进程,但我认为这是 Go 1.16 中的一个 bug。我会让 Go 团队解决我们的分歧。
在这种情况下的问题是,Go 程序在这里 不知道 应该传播哪些句柄,所以没有简单的方法来设置 AdditionalInheritedHandles。
我同意。您的程序设计假设可继承的句柄会被传递给子进程。
在 Go 1.16 中,这没问题。在 Go 1.17 中,我们不得不复制 exec_windows.go 的一大块内容(https://chromium-review.googlesource.com/c/infra/luci/luci-go/+/3739699)。
我很抱歉您不得不为您的程序添加额外的代码。但也许这是一个足够好的解决方案。您是第一个抱怨这个改变的人。也许 Go 需要为像您这样的用户提供一些通用解决方案。我不知道。
提供一个函数可能是一个合理的妥协,该程序可以调用该函数来填充 AdditionalInheritedHandles,其中包含旧版本中可能存在的任何句柄
我不知道如何枚举所有可继承的进程句柄。也许其他人有一些建议。
Alex

9njqaruj

9njqaruj6#

@riannucci
Completely setting aside that this change breaks go1compat
There are no guarantees on the syscall package:
It is impossible to guarantee long-term compatibility with operating system interfaces, which are changed by outside parties. The syscall package is therefore outside the purview of the guarantees made here.

u7up0aaq

u7up0aaq7#

无法保证与操作系统接口的长期兼容性,这些接口会被外部方更改。因此,syscall包不在本保证范围内。
虽然确实如此,操作系统接口会被外部方更改,
1.但这种对系统调用行为的变化似乎并非由底层Windows API的破坏性更改所引发
1.这种变化表现为os/exec包(即Cmd.Start/Cmd.Run)的行为更改,这将是go1compat保证的一部分,不是吗?

ct2axkht

ct2axkht8#

我不了解任何合理的方法来检索所有继承的进程句柄,因此 ossyscall 目前都无法支持 @riannucci 在 #53652(评论)中描述的流程。我认为这个用例非常有效,我们应该提供一种启用它的方法。

我的建议是选择旧的行为,即拥有一个全局锁来保护进程创建,并且不要明确指定要由进程继承的句柄列表。用户可以通过设置一个名为 InheritAllInheritableHandles 的 syscall.SysProcAttr 属性来选择这种行为:

type SysProcAttr struct {
        // ...
	NoInheritHandles           bool                // if set, each inheritable handle in the calling process is not inherited by the new process
	AdditionalInheritedHandles []Handle            // a list of additional handles, already marked as inheritable, that will be inherited by the new process
	ParentProcess              Handle              // if non-zero, the new process regards the process given by this handle as its parent process, and AdditionalInheritedHandles, if set, should exist in this parent process
	// New!
	InheritAllInheritableHandles bool // if set and NoInheritHandles is unset, each inheritable handle in the calling process is inherited by the new process
}

我会提交一个新的提案问题,因为这会添加一个新的API。

相关问题