OSError:[Errno 22]子进程中的参数无效

Fri*_*igg 12 python windows subprocess python-3.x

Python 3.3.3 Windows 7

Here is the full stack:
Traceback (most recent call last):
  File "Blah\MyScript.py", line 578, in Call
    output = process.communicate( input=SPACE_KEY, timeout=600 )
  File "C:\Python33\lib\subprocess.py", line 928, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "C:\Python33\lib\subprocess.py", line 1202, in _communicate
    self.stdin.write(input)
OSError: [Errno 22] Invalid argument
Run Code Online (Sandbox Code Playgroud)

代码如下所示:

process = subprocess.Popen( arguments,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
                env=environment )

output = process.communicate( input=SPACE_KEY, timeout=600 )
Run Code Online (Sandbox Code Playgroud)

这段代码每天运行数百次没有问题.但是如果在同一台机器上运行多个脚本(相同的脚本,但有时来自不同的文件夹),我会收到错误.脚本没有执行相同的操作(即:当我收到此错误时,另一个脚本没有执行子进程).

subProcess代码使用许多不同的命令行来引发错误.

那么,任何人都知道发生了什么?解释器是否有多个执行问题(在不同的进程中)?相同的代码通常可以正常工作,如果解释器运行相同(或非常相似)的脚本,则会破解.但它们通常执行脚本的不同部分.

我很茫然:在8核机器上使用单个处理器很烦人.

Jan*_*cke 8

@eryksun非常好地分析了这个问题的核心,但我认为他的答案中缺少更大的图景(可能是显而易见的事情).

那么,任何人都知道发生了什么?

您的代码遇到竞争条件.有时,您的子进程的行为与您认为的行为不同.

OSError: [Errno 22] Invalid argument在您尝试写入命名管道之前 退出的情况会在您的情况下引发,communicate该管道subprocess已在您的子进程的父级和标准输入之间建立.

Popen()在引擎盖下做了很多.在您的情况下,它首先创建三个命名管道(in _get_handles()),via _winapi.CreatePipe(),每个stdin/out/err一个.然后,它使用生成子进程(in _execute_child())_winapi.CreateProcess().

_execute_child()完成清理程序.记住:所有这一切都发生在Popen().

只有在Popen()返回之后,父进程中的Python VM才会继续调用output = process.communicate(input=SPACE_KEY, timeout=600)

假设你是一个多核系统上,你的系统有可让孩子过程中做了一些工作,而你的Python解释器仍然是时间片的执行Popen().

也就是说,有一个狭窄的时间窗口之间_winapi.CreateProcess()和Python试图通过写孩子的标准输入(后子进程做了一些工作)communicate()(这使得对Windows的一个电话WriteFile,因为eryksun很好地解释).

当您的孩子在时间窗口退出时,您将检索命名错误.

为什么您的子进程比预期更早退出?只有你可以告诉.显然,它并不总是等待来自stdin的数据.


Ery*_*Sun 5

以前communicateEPIPE在写入进程时忽略了错误stdin.从3.3.5开始,每期19612,EINVAL如果孩子已经退出,它也会忽略(22)(参见Lib/subprocess.py第1199行).

背景:

process.communiciate调用process.stdin.write,调用io.FileIO.write,在Windows上调用C运行库_write,调用Win32 WriteFile(在本例中调用NtWriteFile,调用,调用NamedPipe文件系统,作为IRP_MJ_WRITEFastIoWrite).

如果后者失败,它会在线程中设置Windows 系统错误代码.在这种情况下,基础Windows错误可能是ERROR_NO_DATA(232),因为子进程已经退出.C运行时将其映射到errnoEINVAL(22).然后由于_write失败,根据当前值FileIO.write加注.OSErrorerrno


附录:

如果CRT改为映射ERROR_NO_DATA到,则根本不存在问题EPIPE.Python自己的Windows错误翻译通常遵循CRT,但是根据问题13063,它映射ERROR_NO_DATAEPIPE(32)是一个例外.因此,如果孩子已经退出,就_winapi.WriteFile加注BrokenPipeError.

以下示例EINVAL在子进程已退出的情况下复制错误.它还显示了_winapi.WriteFile(3.3.3源链接)将如何映射此错误EPIPE.IMO,这应该被认为是微软CRT中的一个错误.

>>> cmd = 'reg query hkcu'                                                
>>> process = Popen(cmd, stdin=PIPE, stdout=PIPE, universal_newlines=True)
>>> process.stdin.write(' ')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument

>>> hstdin = msvcrt.get_osfhandle(process.stdin.fileno())
>>> _winapi.WriteFile(hstdin, b' ')                                       
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
BrokenPipeError: [WinError 232] The pipe is being closed
Run Code Online (Sandbox Code Playgroud)

  • @JFSebastian,我只能从`process.stdout`管道获取`ERROR_BROKEN_PIPE`.在这种情况下,在`ntdll!NtReadFile`上设置断点显示状态代码为`STATUS_PIPE_BROKEN`.所以我认为`WriteFile`文档要么是令人失望的,要么是完全错误的.看起来有人写了文档的这一部分和[`ReadFile`]的相应部分(https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29 .aspx)同时并没有实际检查`WriteFile`的错误是不同的. (2认同)

mrk*_*afk 0

对于您正在使用的操作系统,该命令 (args) 可能不正确。尝试清理它们(仅检查/传递允许的字符)。