spawn,exec,execFile和fork關(guān)系
exec
│
execFile fork
│ │
└─────spawn─────┘ 用戶層面
│
-----------│------------------------
│
│ nodejs內(nèi)部
│
spawn(位于lib/internal/child_process.js)
從上圖可以看出伊磺,在用戶層面其他的三個(gè)函數(shù)都調(diào)用了child_process.spwan
子進(jìn)程的stdio(標(biāo)準(zhǔn)輸入輸出)
- options.stdio的值是字符串時(shí),有以下幾種取值
- pipe:相當(dāng)于['pipe', 'pipe', 'pipe']屈芜,子進(jìn)程的stdio和父進(jìn)程的stdio通過(guò)管道進(jìn)行連接
- ignore:相當(dāng)于['ignore','ignore', 'ignore']宙帝,子進(jìn)程的stdio綁定到
/dev/null
,丟棄數(shù)據(jù)的輸入輸出妇蛀。 - inherit:繼承父進(jìn)程相關(guān)的stdio,等同于
[process.stdin,process.stdout,process.sterr]
或者[0,1,2]
,此時(shí)子進(jìn)程的stdio都是綁定在同一個(gè)地方祥山。
- 當(dāng)options.stdio值是數(shù)組的時(shí)候蜕劝,前三個(gè)元素分別代表stdin,stdout,stderr袁梗。如果數(shù)組的元素大于3觉吭,
則會(huì)在父子進(jìn)程之間建立額外的通訊通道
腾供,他們可能是下面的值.
- pipe 通過(guò)管道進(jìn)行通訊,管道兩端分別連接父子進(jìn)程鲜滩,父進(jìn)程可以通過(guò)
subprocess.stdin,subprocess.stdout,subprocess.stderr
來(lái)引用管道的一端伴鳖。子進(jìn)程可以通過(guò)process.stdin, process.stdout, process.stderr
來(lái)引用另外一端 - ipc:額外的通訊通道,通過(guò)ipc channel通訊
- stream:通過(guò)nodejs 的stream 對(duì)象通訊徙硅,對(duì)象底層文件描述符代表一個(gè)文件榜聂。如:
socket,tty,本地文件
等 - null, undefined,對(duì)于前三個(gè)元素嗓蘑,他們會(huì)設(shè)置為pipe,剩下的元素會(huì)設(shè)置為ignore
例子
- pipe
綁定到父進(jìn)程的process.stdio中
const child_process = require("child_process");
const script = (function(data) {
console.log(data);
}).toString();
const node = child_process.spawn("node", ["-e", `( ${script}("pipe须肆,只能通過(guò)父進(jìn)程將它輸出") )`], {
stdio: 'pipe'
});
node.stdout.on('data', data => {
console.log(data.toString())
})
- inherit
const child_process = require("child_process");
const script = (function(data) {
console.log(data);
}).toString();
child_process.spawn("node", ["-e", `( ${script}("inherit,這一般個(gè)會(huì)寫(xiě)到控制臺(tái)") )`], {
stdio: [process.stdin, process.stdout, process.stderr]
// 和 stdio: 'inherit'相似
});
- 綁定文件描述符(fd)
const child_process = require("child_process");
const fd = require("fs").openSync("./node.log", "w+");
const script = (function(data) {
console.log(data);
}).toString();
const node = child_process.spawn("node", ["-e", `( ${script}("整數(shù)fd桩皿,這一般個(gè)會(huì)寫(xiě)到某個(gè)文件") )`], {
//stdio綁定到文件描述符fd豌汇,它代表文件node.log,因此會(huì)輸出到該文件
stdio: [process.stdin, fd, fd]
});
- 綁定stream
const child_process = require("child_process");
const fd = require("fs").openSync("./node.log", "w+");
const script = (function(data) {
console.log(data);
}).toString();
const writableStream = require("fs").createWriteStream(null, {
flags: "a",
fd,
});
child_process.spawn("node", ["-e", `( ${script}("stream业簿,這一般個(gè)會(huì)寫(xiě)到某個(gè)文件") )`], {
//輸出到流所代表的目的地瘤礁,注意這個(gè)流必須要有文件描述符,否則會(huì)失敗
//這個(gè)例子中它會(huì)輸出到文件node.log
stdio: [process.stdin, writableStream, fd]
});
- ipc通道
// parent.js
const child_process = require("child_process");
const node = child_process.spawn("node",['./child.js'], {
shell:false,
stdio: ['inherit', 'inherit', 'inherit','ipc']
});
node.send({from: 'parent'});
// child.js
process.on('message', function(m){
console.log('message from parent: ' + JSON.stringify(m));
});
process.send({from: 'child'});
detached 和守護(hù)進(jìn)程
生成子進(jìn)程的時(shí)候如果傳遞了detached=true
,則使得子進(jìn)程成為新的session
和group
的leader
const child_process = require("child_process");
const ping = child_process.spawn("ping", ["localhost"], {
detached:true,
stdio: 'ignore'
});
ping.unref();
需要成為守護(hù)進(jìn)程梅尤,必須要做到以下幾點(diǎn)
- 子進(jìn)程必須和父進(jìn)程分離柜思,即
detached=true
- 默認(rèn)情況下父進(jìn)程會(huì)等待已分離的子進(jìn)程岩调,調(diào)用
subprocess.unref()
來(lái)取消等待 - 子進(jìn)程的IO和父進(jìn)程不能聯(lián)系。使用
stdio: 'ignore'