# 子進(jìn)程
~~~
穩(wěn)定度: 3 - 穩(wěn)定
~~~
Node 通過(guò) `child_process` 模塊提供了類(lèi)似 `popen(3)` 的處理三向數(shù)據(jù)流(stdin/stdout/stderr)的功能。
它能夠以完全非阻塞的方式與子進(jìn)程的 `stdin`、`stdout` 和 `stderr` 以流式傳遞數(shù)據(jù)。(請(qǐng)注意,某些程序在內(nèi)部使用行緩沖 I/O。這不會(huì)影響到 node.js,但您發(fā)送到子進(jìn)程的數(shù)據(jù)不會(huì)被立即消費(fèi)。)
使用 `require('child_process').spawn()`或者 `require('child_process').fork()` 創(chuàng)建子進(jìn)程,這兩種方法的語(yǔ)義有些區(qū)別,下文將會(huì)解釋。
### 類(lèi): ChildProcess
`ChildProcess` 是一個(gè) [EventEmitter](#)。
子進(jìn)程有三個(gè)與之關(guān)聯(lián)的流:`child.stdin`、`child.stdout` 和 `child.stderr`。它們可以共享父進(jìn)程的 stdio 流,也可以作為獨(dú)立的被導(dǎo)流的流對(duì)象。
ChildProcess 類(lèi)不能直接被使用, 使用 `spawn()` 或者 `fork()` 方法創(chuàng)建一個(gè) Child Process 實(shí)例。
### 事件: 'error'
- `err` {Error Object} 錯(cuò)誤。
發(fā)生于:
1. 進(jìn)程不能被創(chuàng)建, 或者
1. 進(jìn)程不能被終止掉, 或者
1. 由任何原因引起的數(shù)據(jù)發(fā)送到子進(jìn)程失敗.
參閱 [`ChildProcess#kill()`](#) 和 [`ChildProcess#send()`](#)。
### 事件: 'exit'
- `code` {Number} 假如進(jìn)程正常退出,則為它的退出代碼。
- `signal` {String} 假如是被父進(jìn)程終止,則為所傳入的終止子進(jìn)程的信號(hào)。
這個(gè)事件是在子進(jìn)程被結(jié)束的時(shí)候觸發(fā)的. 假如進(jìn)程被正常結(jié)束,‘code’就是退出進(jìn)程的指令代碼, 否則為'null'. 假如進(jìn)程是由于接受到signal結(jié)束的, `signal` 就代表著信號(hào)的名稱(chēng), 否則為`null`.
注意子進(jìn)程的 stdio 流可能仍為開(kāi)啟狀態(tài)。
參閱`waitpid(2)`.
### 事件: 'close'
- `code` {Number} 假如進(jìn)程正常退出,則為它的退出代碼。
- `signal` {String} 假如是被父進(jìn)程終止,則為所傳入的終止子進(jìn)程的信號(hào)。
這個(gè)事件會(huì)在一個(gè)子進(jìn)程的所有stdio流被終止時(shí)觸發(fā), 這和'exit'事件有明顯的不同,因?yàn)槎噙M(jìn)程有時(shí)候會(huì)共享同一個(gè)stdio流
### 事件: 'disconnect'
在子進(jìn)程或父進(jìn)程中使用使用.disconnect()方法后,這個(gè)事件會(huì)被觸發(fā),在斷開(kāi)之后,就不可能再相互發(fā)送信息了。可以通過(guò)檢查子進(jìn)程的child.connected屬性是否為true去檢查是否可以發(fā)送信息
### 事件: 'message'
- `message` {Object} 一個(gè)已解析的JSON對(duì)象或者原始類(lèi)型值
- `sendHandle` {Handle object} 一個(gè)socket 或者 server對(duì)象
通過(guò).send()發(fā)送的信息可以通過(guò)監(jiān)聽(tīng)'message'事件獲取到
### child.stdin
- {Stream object}
子進(jìn)程的'stdin'是一個(gè)‘可寫(xiě)流’,通過(guò)end()方法關(guān)閉該可寫(xiě)流可以終止子進(jìn)程,
假如子進(jìn)程的stdio流與父線程共享,這個(gè)child.stdin不會(huì)被設(shè)置
### child.stdout
- {Stream object}
子進(jìn)程的`stdout`是個(gè)可讀流.
假如子進(jìn)程的stdio流與父線程共享,這個(gè)child.stdin不會(huì)被設(shè)置
### child.stderr
- {Stream object}
子進(jìn)程的stderr是一個(gè)可讀流
假如子進(jìn)程的stdio流與父線程共享,這個(gè)child.stdin不會(huì)被設(shè)置
### child.pid
- {Integer}
子進(jìn)程的PID
實(shí)例:
~~~
console.log('Spawned child pid: ' + grep.pid);
grep.stdin.end();
~~~
### child.kill([signal])
- `signal` {String}
發(fā)送一個(gè)信號(hào)給子線程. 假如沒(méi)有給參數(shù), 將會(huì)發(fā)送 `'SIGTERM'`. 參閱 `signal(7)` 查看所有可用的signals列表
~~~
// send SIGHUP to process
grep.kill('SIGHUP');
~~~
當(dāng)一個(gè)signal不能被傳遞的時(shí)候,會(huì)觸發(fā)一個(gè)'error'事件, 發(fā)送一個(gè)信號(hào)到已終止的子線程不會(huì)發(fā)生錯(cuò)誤,但是可能引起不可預(yù)見(jiàn)的后果, 假如該子進(jìn)程的ID已經(jīng)重新分配給了其他進(jìn)程,signal將會(huì)被發(fā)送到其他進(jìn)程上面,大家可以猜想到這發(fā)生什么后果。
注意,當(dāng)函數(shù)調(diào)用‘kill’, 傳遞給子進(jìn)程的信號(hào)不會(huì)去終結(jié)子進(jìn)程, ‘kill’實(shí)際上只是發(fā)送一個(gè)信號(hào)到進(jìn)程而已。
See `kill(2)`
### child.send(message, [sendHandle])
- `message` {Object}
- `sendHandle` {Handle object}
當(dāng)使用 `child_process.fork()` 你可以使用 `child.send(message, [sendHandle])`向子進(jìn)程寫(xiě)數(shù)據(jù) and 數(shù)據(jù)將通過(guò)子進(jìn)程上的‘message’事件接受.
例如:
~~~
n.send({ hello: 'world' });
~~~
然后是子進(jìn)程腳本的代碼, `'sub.js'` 代碼如下:
~~~
process.send({ foo: 'bar' });
~~~
在子進(jìn)程腳本中'process'對(duì)象有‘send()’方法, ‘process’每次通過(guò)它的信道接收到信息都會(huì)觸發(fā)事件,信息以對(duì)象形式返回。
不過(guò)發(fā)送`{cmd: 'NODE_foo'}` 信息是個(gè)比較特殊的情況. 所有在‘cmd’屬性中包含 a `NODE_`前綴的信息將不會(huì)觸發(fā)‘message’事件, 因?yàn)樗麄兪怯蒼ode 核心使用的內(nèi)部信息. 相反這種信息會(huì)觸發(fā) `internalMessage` 事件, 你應(yīng)該通過(guò)各種方法避免使用這種特性, 他改變的時(shí)候不會(huì)接收到通知.
`child.send()`的`sendHandle` 選項(xiàng)是用來(lái)發(fā)送一個(gè)TCP服務(wù)或者socket對(duì)象到另一個(gè)線程的,子進(jìn)程將會(huì)接收這個(gè)參數(shù)作為‘message’事件的第二個(gè)參數(shù)。
假如信息不能被發(fā)送,將會(huì)觸發(fā)一個(gè)‘error’事件, 比如說(shuō)因?yàn)樽泳€程已經(jīng)退出了。
#### 例子: 發(fā)送一個(gè)server對(duì)象
這里是一個(gè)發(fā)送一個(gè)server對(duì)象的例子:
~~~
// 創(chuàng)建一個(gè)handle對(duì)象,發(fā)送一個(gè)句柄.
var server = require('net').createServer();
server.on('connection', function (socket) {
socket.end('handled by parent');
});
server.listen(1337, function() {
child.send('server', server);
});
~~~
同時(shí)子進(jìn)程將會(huì)以如下方式接收到這個(gè)server對(duì)象:
~~~
process.on('message', function(m, server) {
if (m === 'server') {
server.on('connection', function (socket) {
socket.end('handled by child');
});
}
});
~~~
注意,server對(duì)象現(xiàn)在有父進(jìn)程和子進(jìn)程共享,這意味著某些連接將會(huì)被父進(jìn)程和子進(jìn)程處理。
對(duì)‘dgram’服務(wù)器,工作流程是一樣的, 你監(jiān)聽(tīng)的是‘message’事件,而不是 ‘connection’事件, 使用‘server.bind’ ,而不是‘server.listen’.(當(dāng)前僅在UNIX平臺(tái)支持)
#### 示例: 發(fā)送socket對(duì)象
這是個(gè)發(fā)送socket的例子. 他將創(chuàng)建兩個(gè)子線程 ,同時(shí)處理連接,這是通過(guò)使用遠(yuǎn)程地址 `74.125.127.100` 作為 VIP 發(fā)送socket到一個(gè)‘特殊’的子線程. 其他的socket將會(huì)發(fā)送到‘正?!木€程里.
~~~
// if this is a VIP
if (socket.remoteAddress === '74.125.127.100') {
special.send('socket', socket);
return;
}
// just the usual dudes
normal.send('socket', socket);
});
server.listen(1337);
~~~
`child.js` 文件代碼如下:
~~~
process.on('message', function(m, socket) {
if (m === 'socket') {
socket.end('You were handled as a ' + process.argv[2] + ' person');
}
});
~~~
注意,一旦單個(gè)的socket被發(fā)送到子進(jìn)程,當(dāng)這個(gè)socket被刪除之后,父進(jìn)程將不再對(duì)它保存跟蹤,這表明了這個(gè)條件下‘.connetions’屬性將變成'null', 在這個(gè)條件下同時(shí)也不推薦使用‘.maxConnections’屬性.
### child.disconnect()
使用`child.disconnect()` 方法關(guān)閉父進(jìn)程與子進(jìn)程的IPC連接. 他讓子進(jìn)程非常優(yōu)雅的退出,因?yàn)橐呀?jīng)沒(méi)有活躍的IPC信道. 當(dāng)調(diào)用這個(gè)方法,‘disconnect’事件將會(huì)同時(shí)在父進(jìn)程和子進(jìn)程內(nèi)被觸發(fā),‘connected’的標(biāo)簽將會(huì)被設(shè)置成‘flase’, 請(qǐng)注意,你也可以在子進(jìn)程中調(diào)用‘process.disconnect()’
### child_process.spawn(command, [args], [options])
- `command` {String}要運(yùn)行的命令
- `args` {Array} 字符串參數(shù)列表
- `options` {Object}
- `cwd` {String} 子進(jìn)程的當(dāng)前的工作目錄
- `stdio` {Array|String} 子進(jìn)程 stdio 配置. (參閱下文)
- `customFds` {Array} **Deprecated** 作為子進(jìn)程 stdio 使用的 文件標(biāo)示符. (參閱下文)
- `env` {Object} 環(huán)境變量的鍵值對(duì)
- `detached` {Boolean} 子進(jìn)程將會(huì)變成一個(gè)進(jìn)程組的領(lǐng)導(dǎo)者. (參閱下文)
- `uid` {Number} 設(shè)置用戶進(jìn)程的ID. (See setuid(2).)
- `gid` {Number} 設(shè)置進(jìn)程組的ID. (See setgid(2).)
- 返回: {ChildProcess object}
用給定的命令發(fā)布一個(gè)子進(jìn)程,帶有‘a(chǎn)rgs’命令行參數(shù),如果省略的話,‘a(chǎn)rgs’默認(rèn)為一個(gè)空數(shù)組
第三個(gè)參數(shù)被用來(lái)指定額外的設(shè)置,默認(rèn)是:
~~~
{ cwd: undefined,
env: process.env
}
~~~
`cwd`允許你從被創(chuàng)建的子進(jìn)程中指定一個(gè)工作目錄. 使用 `env` 去指定在新進(jìn)程中可用的環(huán)境變量.
一個(gè)運(yùn)行 `ls -lh /usr`的例子, 獲取`stdout`, `stderr`, 和退出代碼:
~~~
ls.on('close', function (code) {
console.log('child process exited with code ' + code);
});
~~~
例子: 一個(gè)非常精巧的方法執(zhí)行 'ps ax | grep ssh'
~~~
grep.on('close', function (code) {
if (code !== 0) {
console.log('grep process exited with code ' + code);
}
});
~~~
檢查執(zhí)行錯(cuò)誤的例子:
~~~
child.stderr.setEncoding('utf8');
child.stderr.on('data', function (data) {
if (/^execvp\(\)/.test(data)) {
console.log('Failed to start child process.');
}
});
~~~
注意,當(dāng)在spawn過(guò)程中接收一個(gè)空對(duì)象,這會(huì)導(dǎo)致創(chuàng)建的進(jìn)程使用空的環(huán)境變量而不是使用‘process.env’.這是由于與一個(gè)廢棄API向后兼容的問(wèn)題.
`child_process.spawn()` 中的 `stdio` 選項(xiàng)是一個(gè)數(shù)組,每個(gè)索引對(duì)應(yīng)子進(jìn)程中的一個(gè)文件標(biāo)識(shí)符。可以是下列值之一:
1.
`'pipe'` -在子進(jìn)程與父進(jìn)程之間創(chuàng)建一個(gè)管道,管道的父進(jìn)程端以 `child_process` 的屬性的形式暴露給父進(jìn)程,如 `ChildProcess.stdio[fd]`。 為 文件標(biāo)識(shí)(fds) 0 - 2 建立的管道也可以通過(guò) ChildProcess.stdin,ChildProcess.stdout 及 ChildProcess.stderr 分別訪問(wèn)。
1.
`'ipc'` - 創(chuàng)建一個(gè)IPC通道以在父進(jìn)程與子進(jìn)程之間傳遞 消息/文件標(biāo)識(shí)符。一個(gè)子進(jìn)程只能有最多*一個(gè)* IPC stdio 文件標(biāo)識(shí)。 設(shè)置該選項(xiàng)激活 ChildProcess.send() 方法。如果子進(jìn)程向此文件標(biāo)識(shí)符寫(xiě)JSON消息,則會(huì)觸發(fā) ChildProcess.on("message")。 如果子進(jìn)程是一個(gè)nodejs程序,那么IPC通道的存在會(huì)激活process.send()和process.on('message')
1.
`'ignore'` - 不在子進(jìn)程中設(shè)置該文件標(biāo)識(shí)。注意,Node 總是會(huì)為其spawn的進(jìn)程打開(kāi) 文件標(biāo)識(shí)(fd) 0 - 2。 當(dāng)其中任意一項(xiàng)被 ignored,node 會(huì)打開(kāi) `/dev/null` 并將其附給子進(jìn)程的文件標(biāo)識(shí)(fd)。
1.
`Stream` 對(duì)象 - 與子進(jìn)程共享一個(gè)與tty,文件,socket,或者管道(pipe)相關(guān)的可讀或可寫(xiě)流。 該流底層(underlying)的文件標(biāo)識(shí)在子進(jìn)程中被復(fù)制給stdio數(shù)組索引對(duì)應(yīng)的文件標(biāo)識(shí)(fd)
1.
正數(shù) - 該整形值被解釋為父進(jìn)程中打開(kāi)的文件標(biāo)識(shí)符。他與子進(jìn)程共享,和`Stream`被共享的方式相似。
1.
`null`, `undefined` - 使用默認(rèn)值。 對(duì)于stdio fds 0,1,2(或者說(shuō)stdin,stdout和stderr),pipe管道被建立。對(duì)于fd 3及往后,默認(rèn)為`ignore`
作為快捷方式,`stdio` 參數(shù)除了數(shù)組也可以是下列字符串之一:
- `ignore` - `['ignore', 'ignore', 'ignore']`
- `pipe` - `['pipe', 'pipe', 'pipe']`
- `inherit` - `[process.stdin, process.stdout, process.stderr]` 或 `[0,1,2]`
實(shí)例:
~~~
// 開(kāi)啟一個(gè)額外的 fd=4 來(lái)與提供 startd 風(fēng)格接口的程序進(jìn)行交互。
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });
~~~
如果 `detached` 選項(xiàng)被設(shè)置,則子進(jìn)程會(huì)被作為新進(jìn)程組的 leader。這使得子進(jìn)程可以在父進(jìn)程退出后繼續(xù)運(yùn)行。
缺省情況下,父進(jìn)程會(huì)等待脫離了的子進(jìn)程退出。要阻止父進(jìn)程等待一個(gè)給出的子進(jìn)程 `child`,使用 `child.unref()` 方法,則父進(jìn)程的事件循環(huán)引用計(jì)數(shù)中將不會(huì)包含這個(gè)子進(jìn)程。
脫離一個(gè)長(zhǎng)時(shí)間運(yùn)行的進(jìn)程并將它的輸出重定向到一個(gè)文件的例子:
~~~
child.unref();
~~~
當(dāng)使用 `detached` 選項(xiàng)來(lái)啟動(dòng)一個(gè)長(zhǎng)時(shí)間運(yùn)行的進(jìn)程,該進(jìn)程不會(huì)在后臺(tái)保持運(yùn)行,除非向它提供了一個(gè)不連接到父進(jìn)程的 `stdio` 配置。如果繼承了父進(jìn)程的 `stdio`,則子進(jìn)程會(huì)繼續(xù)附著在控制終端。
有一個(gè)已廢棄的選項(xiàng) `customFds` 允許指定特定文件描述符作為子進(jìn)程的 stdio。該 API 無(wú)法移植到所有平臺(tái),因此被移除。使用 `customFds` 可以將新進(jìn)程的 `[stdin, stdout, stderr]` 鉤到已有流上;`-1` 表示創(chuàng)建新流。自己承擔(dān)使用風(fēng)險(xiǎn)。
參閱:`child_process.exec()` 和 `child_process.fork()`
### child_process.exec(command, [options], callback)
- `command` {String} 將要執(zhí)行的命令,用空格分隔參數(shù)
- `options` {Object}
- `cwd` {String} 子進(jìn)程的當(dāng)前工作目錄
- `env` {Object} 環(huán)境變量鍵值對(duì)
- `encoding` {String} 編碼(缺省為 'utf8')
- `shell` {String} 運(yùn)行命令的 shell(UNIX 上缺省為 '/bin/sh',Windows 上缺省為 'cmd.exe'。該 shell 在 UNIX 上應(yīng)當(dāng)接受 `-c` 開(kāi)關(guān),在 Windows 上應(yīng)當(dāng)接受 `/s /c` 開(kāi)關(guān)。在 Windows 中,命令行解析應(yīng)當(dāng)兼容 `cmd.exe`。)
- `timeout` {Number} 超時(shí)(缺省為 0)
- `maxBuffer` {Number} 最大緩沖(缺省為 200*1024)
- `killSignal` {String} 結(jié)束信號(hào)(缺省為 'SIGTERM')
- `callback` {Function} 進(jìn)程結(jié)束時(shí)回調(diào)并帶上輸出
- `error` {Error}
- `stdout` {Buffer}
- `stderr` {Buffer}
- 返回:ChildProcess 對(duì)象
在 shell 中執(zhí)行一個(gè)命令并緩沖輸出。
~~~
child = exec('cat *.js bad_file | wc -l',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
~~~
回調(diào)參數(shù)為 `(error, stdout, stderr)`。當(dāng)成功時(shí),`error` 會(huì)是 `null`。當(dāng)遇到錯(cuò)誤時(shí),`error` 會(huì)是一個(gè) `Error` 實(shí)例,并且 `err.code` 會(huì)是子進(jìn)程的退出代碼,同時(shí) `err.signal` 會(huì)被設(shè)置為結(jié)束進(jìn)程的信號(hào)名。
第二個(gè)可選的參數(shù)用于指定一些選項(xiàng),缺省選項(xiàng)為:
~~~
{ encoding: 'utf8',
timeout: 0,
maxBuffer: 200*1024,
killSignal: 'SIGTERM',
cwd: null,
env: null }
~~~
如果 `timeout` 大于 0,則當(dāng)進(jìn)程運(yùn)行超過(guò) `timeout` 毫秒后會(huì)被終止。子進(jìn)程使用 `killSignal` 信號(hào)結(jié)束(缺省為 `'SIGTERM'`)。`maxBuffer` 指定了 stdout 或 stderr 所允許的最大數(shù)據(jù)量,如果超出這個(gè)值則子進(jìn)程會(huì)被終止。
### child_process.execFile(file, args, options, callback)
- `file` {String} 要運(yùn)行的程序的文件名
- `args` {Array} 字符串參數(shù)列表
- `options` {Object}
- `cwd` {String} 子進(jìn)程的當(dāng)前工作目錄
- `env` {Object} 環(huán)境變量鍵值對(duì)
- `encoding` {String} 編碼(缺省為 'utf8')
- `timeout` {Number} 超時(shí)(缺省為 0)
- `maxBuffer` {Number} 最大緩沖(缺省為 200*1024)
- `killSignal` {String} 結(jié)束信號(hào)(缺省為 'SIGTERM')
- `callback` {Function} 進(jìn)程結(jié)束時(shí)回調(diào)并帶上輸出
- `error` {Error}
- `stdout` {Buffer}
- `stderr` {Buffer}
- 返回:ChildProcess 對(duì)象
該方法類(lèi)似于 `child_process.exec()`,但是它不會(huì)執(zhí)行一個(gè)子 shell,而是直接執(zhí)行所指定的文件。因此它稍微比 `child_process.exec` 精簡(jiǎn),參數(shù)與之一致。
### child_process.fork(modulePath, [args], [options])
- `modulePath` {String} 子進(jìn)程中運(yùn)行的模塊
- `args` {Array} 字符串參數(shù)列表
- `options` {Object}
- `cwd` {String} 子進(jìn)程的當(dāng)前工作目錄
- `env` {Object} 環(huán)境變量鍵值對(duì)
- `encoding` {String} 編碼(缺省為 'utf8')
- `execPath` {String} 創(chuàng)建子進(jìn)程的可執(zhí)行文件
- 返回:ChildProcess 對(duì)象
該方法是 `spawn()` 的特殊情景,用于派生 Node 進(jìn)程。除了普通 ChildProcess 實(shí)例所具有的所有方法,所返回的對(duì)象還具有內(nèi)建的通訊通道。詳見(jiàn) `child.send(message, [sendHandle])`。
缺省情況下所派生的 Node 進(jìn)程的 stdout、stderr 會(huì)關(guān)聯(lián)到父進(jìn)程。要更改該行為,可將 `options` 對(duì)象中的 `silent` 屬性設(shè)置為 `true`。
子進(jìn)程運(yùn)行完成時(shí)并不會(huì)自動(dòng)退出,您需要明確地調(diào)用 `process.exit()`。該限制可能會(huì)在未來(lái)版本里接觸。
這些子 Node 是全新的 V8 實(shí)例,假設(shè)每個(gè)新的 Node 需要至少 30 毫秒的啟動(dòng)時(shí)間和 10MB 內(nèi)存,就是說(shuō)您不能創(chuàng)建成百上千個(gè)這樣的實(shí)例。
`options` 對(duì)象中的 `execPath` 屬性可以用非當(dāng)前 `node` 可執(zhí)行文件來(lái)創(chuàng)建子進(jìn)程。這需要小心使用,并且缺省情況下會(huì)使用子進(jìn)程上的 `NODE_CHANNEL_FD` 環(huán)境變量所指定的文件描述符來(lái)通訊。該文件描述符的輸入和輸出假定為以行分割的 JSON 對(duì)象。
- 關(guān)于本文檔
- 概述
- 斷言 (assert)
- Buffer
- Addons插件
- 子進(jìn)程
- 集群
- 控制臺(tái)
- 加密(Crypto)
- 調(diào)試器
- DNS
- 域
- 事件 (Events)
- File System
- 全局對(duì)象
- HTTP
- HTTPS
- Modules
- net
- 操作系統(tǒng)
- 路徑 (Path)
- process
- punycode
- Query String
- Readline
- REPL
- Smalloc
- 流
- 字符串解碼器
- 定時(shí)器
- TLS (SSL)
- TTY
- UDP / 數(shù)據(jù)報(bào)套接字
- URL
- utils
- 執(zhí)行 JavaScript
- Zlib
- 進(jìn)度
- 感謝
