更新3:此问题似乎是node-sass的模块安装的一部分 . 搁浅的进程的工作目录为 ./node_modules/node-sass
,其命令行 scripts/install.js
解析为模块内的文件 . 此外,到达控制台的最后一行输出与node-sass' scripts/install.js
的行输出匹配:
if (cachedBinary) {
console.log('Cached binary found at', cachedBinary);
fs.createReadStream(cachedBinary).pipe(fs.createWriteStream(binaryPath));
return;
}
从命令行运行时,此代码没有任何问题(即,只需在命令提示符下使用空白的 node_modules
目录发出 npm install
),但是当 npm install
通过 popen3
启动时,此处的流 .pipe
调用似乎无限期地阻塞 .
这对我来说是一个令人头疼的问题......
如果I ^ C是Ruby启动这些子进程的终端,则中断会使其进入恶意进程并导致它终止 . 但是,强制关闭所有管道(或简单地终止父进程)不会导致流氓 node.exe
退出 .
我考虑了一个替代版本的 popen3
明确地等待子进程,而不是只是隐式地等待所有流都结束,但是虽然这确实允许调用端正常进行,但是流氓子进程仍然挂起,并且通过保持 ./node_modules/node-sass
目录的打开句柄来干扰后续调用 .
更新4:我用 node-sass
项目打开了这个错误报告:https://github.com/sass/node-sass/issues/2459
更新:我很确定这实际上是一个Node问题 . 我追踪了挂起的根本原因,并且通过一个复杂的子进程树, npm install
最终留下了一个 node.exe
的实例,它只是坐在那里,显然无限期地无所事事,保留了它继承的 stdout
和 stderr
管道打开 .
所以,这留下了新的问题:
-
有没有办法让Node在
npm install
完成后不会留下一个落后者进程? -
有没有办法明确等待
popen3
的直接子进程退出,而不是等待流结束,然后可能关闭来自侦听端的流来终止抽取输出的线程?
更新2:我用这个极简主义代码重现了这个问题:
Open3::popen3 "npm install" do |stdin, stdout, stderr, thr|
stdin.close
stdout.each_line { |l| puts l }
end
使用此代码, npm install
完成后,流氓 node.exe
进程(命令行: scripts/install.js
)会挂起 . 终止进程取消阻止 popen3
调用(通过使 stdout
结束,所以 each_line
循环终止),并且^ Cing Ruby代码(当在IRB窗口中运行时)导致流氓 node.exe
终止(在控制台输出: => #<IO:(closed)>
) .
仅当进程通过 popen3
运行时才会发生这种情况;来自CMD提示符的相同 npm install
正常退出 .
原始问题:
我在Ruby脚本中遇到了 popen3
的问题 . 它's hanging, but I'我很确定's not any of the usual candidates. I'已经用大量的注释更新了我对 popen3
的调用,这样我就可以在控制台输出中看到's going on. Here is how I' m正在拨打电话:
command_output_lines = []
lock = Mutex.new
exit_code = nil
Logger.log("[MAIN] beginning popen3 block")
Open3.popen3(command_w_params) do |stdin, stdout, stderr, thr|
Logger.log("[MAIN] closing stdin stream")
stdin.close
Logger.log("[MAIN] starting [STDOUT]")
stdout_thread = Thread.new do
Logger.log("[STDOUT] started")
begin
stdout.each_line do |stdout_line|
Logger.log("[STDOUT] got a line, acquiring lock")
lock.synchronize do
command_output_lines <<= stdout_line
Logger.log(stdout_line)
end
Logger.log("[STDOUT] lock released")
end
rescue Exception => e
Logger.log("[STDOUT] exception: #{e}")
end
Logger.log("[STDOUT] exiting")
end
Logger.log("[MAIN] starting [STDERR]")
stderr_thread = Thread.new do
Logger.log("[STDERR] started")
begin
stderr.each_line do |stderr_line|
Logger.log("[STDERR] got a line, acquiring lock")
lock.synchronize do
command_output_lines <<= "[STDERR] " + stderr_line
Logger.warn(stderr_line)
end
Logger.log("[STDERR] lock released")
end
rescue Exception => e
Logger.log("[STDERR] exception: #{e}")
end
Logger.log("[STDERR] exiting")
end
Logger.log("[MAIN] joining to [STDOUT]")
stdout_thread.join
Logger.log("[MAIN] joining to [STDERR]")
stderr_thread.join
Logger.log("[MAIN] threads joined, reading exit status")
exit_code = thr.value.exitstatus
end
Logger.log("[MAIN] popen3 block completed")
(不要在意 Logger.log
究竟是什么;只知道它将输出发送到控制台 . )
在我看到问题的地方, command_w_params
等于 npm install
,并且此代码在 bundle exec rake TaskName
的上下文中运行 .
当它到达此代码时,我看到以下控制台输出:
[MAIN] beginning popen3 block
[MAIN] closing stdin stream
[MAIN] starting [STDOUT]
[MAIN] starting [STDERR]
[MAIN] joining to [STDOUT]
[STDOUT] started
[STDERR] started
[STDOUT] got a line, acquiring lock
[STDOUT] lock released
[STDOUT] got a line, acquiring lock
> node-sass@4.9.2 install C:\Users\Jonathan Gilbert\RepositoryName\ProjectName\node_modules\node-sass
[STDOUT] lock released
[STDOUT] got a line, acquiring lock
> node scripts/install.js
[STDOUT] lock released
[STDOUT] got a line, acquiring lock
[STDOUT] lock released
[STDOUT] got a line, acquiring lock
Cached binary found at C:\Users\Jonathan Gilbert\AppData\Roaming\npm-cache\node- sass\4.9.2\win32-x64-57_binding.node
[STDOUT] lock released
......然后它就会挂起来 . 此时,我可以在Process Explorer中看到子进程已退出 . 除了 ruby.exe
之外什么都没有留下,但它只是无限期地坐在那里直到它被明确取消 . 这两个线程仍在运行,表明 stdout
和 stderr
流尚未发出流末尾信号 .
现在,通常当人们对 popen3
有问题时,'s because they'不会同时读取 stdout
和 stderr
,并且其中一个或另一个填充其管道缓冲区,而父进程只关注另一个 . 但我的代码使用单独的线程并保持管道缓冲区为空 .
我看到的另一个问题是,子进程可能会在等待 stdin
被关闭,但在这种情况下:
-
stdin
正在关闭 . -
子进程甚至不再存在 .
有人认出这些症状吗?当子进程退出时,为什么 stdout
和 stderr
流不会到达流末尾?