更新 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;
}
此代码在从命令行运行时没有任何问题(即,只需npm install
在命令提示符下使用空白node_modules
目录发出),但是当npm install
通过 启动时popen3
,这里的流调用似乎.pipe
无限期地阻塞。
这对我来说是一个令人头疼的时刻......
如果我 ^C 是 Ruby 启动这些子进程的终端,则中断会使其进入流氓进程并使其终止。但是,强制关闭所有管道(或简单地终止父进程)不会导致流氓node.exe
退出。
我考虑了一个替代版本,popen3
它显式等待子进程,而不是仅仅隐式等待流全部结束,但是虽然这确实允许调用方正常进行,但流氓子进程仍然徘徊,并且会通过持有目录的打开句柄来干扰后续调用./node_modules/node-sass
。
更新4:我已经打开了这个node-sass
项目的错误报告:https ://github.com/sass/node-sass/issues/2459
更新:我很确定这实际上是一个节点问题。我找到了挂起的根本原因,它是通过一个复杂的子进程树,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
使用此代码,流氓node.exe
进程(命令行scripts/install.js
:)在npm install
完成后挂起。终止进程会解除阻塞popen3
调用(通过导致stdout
结束,因此each_line
循环终止),并且 ^Cing Ruby 代码(在 IRB 窗口中运行时)会导致流氓node.exe
终止(在控制台输出中的一行之后=> #<IO:(closed)>
:) .
这仅在进程运行时发生popen3
;CMD 提示符中的相同npm install
内容正常退出。
原始问题:
我popen3
在 Ruby 脚本中遇到问题。它挂了,但我很确定它不是任何通常的候选人。我已经popen3
用大量注释更新了我的调用,以便我可以在控制台输出中看到正在发生的事情。这是我打电话的方式:
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
对. 但是我的代码使用单独的线程并保持管道缓冲区为空。stdout
stderr
我看到的另一个问题是子进程可能一直在等待stdin
关闭,但在这种情况下:
stdin
正在关闭。- 子进程甚至不再存在。
有人认识这些症状吗?为什么子进程退出时stdout
和stderr
流没有达到流尾?