Node.js子进程和管道
我正在尝试使用管道和Node的子进程模块来获取两个长时间运行的node.js进程以进行通信 - 父级和子级。 我希望孩子能够异步地将数据发送回父母,并且我希望使用管道来这样做。
以下是我的代码的简化版本:
家长:
cp = require('child_process')
es = require('event-stream')
child = cp.spawn('coffee', ['child.coffee'], {stdio: [null, null, null, 'pipe']})
so = child.stdout.pipe(es.split())
p3 = child.stdio[3].pipe(es.split())
so.on 'data', (data) ->
console.log('stdout: ' + data)
child.stderr.on 'data', (data) ->
console.log('stderr: ' + data);
p3.on 'data', (data) ->
console.log('stdio3: ' + data);
child.on 'close', (code) ->
console.log('child process exited with code ' + code)
child.stdin.write "a message from your parent", "utf8"
儿童:
fs = require('fs')
p3 = fs.createWriteStream('/dev/fd/3', {encoding: 'utf8'})
process.stdin.on 'data', (data) ->
p3.write "hello #{process.pid} - #{data}n", 'utf8'
process.stdout.write "world #{process.pid} - #{data}n", 'utf8'
p3.end()
process.exit(0)
process.stdin.on 'end', (data) ->
console.log "end of stdin"
p3.end()
process.exit(0)
process.stdin.setEncoding('utf8')
process.stdin.resume()
该代码适用于OSX 10.9,但无法在Ubuntu盒子上运行。 我已经尝试在Ubuntu 12.04和14.04下运行它。 我正在运行Node 10.2x。
/dev/fd/
在Ubuntu下象征性地链接到/proc/self/fd/
所以我相信我的子进程正在打开正确的文件。
在Ubuntu上运行父级的输出如下所示:
$ coffee parent.coffee
stderr:
stderr: events.js:72
stderr: throw er; // Unhandled 'error' event
stderr:
stderr:
stderr:
stderr:
stderr: ^
stderr: Error: UNKNOWN, open '/dev/fd/3'
events.js:72
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET
at errnoException (net.js:901:11)
at Pipe.onread (net.js:556:19)
我期望看到(并在OSX盒子上做):
$ coffee parent.coffee
stdio3: hello 21101 - a message from your parent
stdout: world 21101 - a message from your parent
stdio3:
stdout:
child process exited with code 0
也可以在Ubuntu上使用命令行与孩子进行交流,所以在产生子进程时父问题可能在父进程中:
$ echo foo | coffee child.coffee 3>&1
hello 3077 - foo
world 3077 - foo
我试图调查节点使用strace进行的内核调用,但无法理解输出。
我自己想清楚了。 错误发生在孩子身上。 在打开已经打开的文件时,Ubuntu linux更加严格,行:
p3 = fs.createWriteStream('/dev/fd/3', {encoding: 'utf8'})
抛出一个错误。 当孩子运行时,文件描述符3
已经打开,所以代码应该如下所示:
儿童:
fs = require('fs')
# parent opens the file descriptor 3 when spawning the child (and closes it when the child returns)
fd3write = (s) ->
b = new Buffer(s)
fs.writeSync(3,b,0,b.length)
process.stdin.on 'data', (data) ->
fd3write "p3 #{process.pid} - #{data}n"
process.stdout.write "so #{process.pid} - #{data}n", 'utf8'
process.exit(0)
process.stdin.on 'end', (data) ->
console.log "end of stdin"
process.exit(0)
process.stdin.setEncoding('utf8')
process.stdin.resume()
我希望这会对别人有所帮助。
要使用管道而不是stdin
将消息从父节点发送到子节点,该链接可能是有用的:child-process-multiple-file-descriptors。