這篇文章主要介紹了從Node.##js的child_process模組來學習父子進程之間的通信,具有一定的參考價值,有興趣的小夥伴可以參考一下。 ,這個功能主要是透過child_process.spawn函數
來提供的:const spawn = require('child_process').spawn; const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.log(`stderr: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code $[code]`); });預設情況下,Node.js進程和子進程之間的stdin,stdout,stderr管道是已經存在的。進程的資料可能不會馬上消費)。 循環
,然而child-process.spawnSync方法是同步的,他會阻塞事件循環只到產生的程序退出或終止。一個shell客戶端,然後使用shell來執行程序,當完成的時候傳遞給
回調函數
child_process.execFile:和exec相似,但是他不會馬上產生一個shell
child_process.fork:產生一個新的Node.js進程,同時執行一個特定的模組來產生IPC通道,進而在父進程和子進程之間傳輸資料
child_process.execSync:和exec不同之處在於會阻塞Node.js的事件循環,然而child-process
API
。每一個方法都會產生一個ChildProcess實例,而且這個
實作了Node.js的EventEmitter這個API,於是父行程可以註冊監聽函數,在子行程的特定事件觸發的時候被呼叫。 child_process.exec() 和 child_process.execFile()可以指定一個可選的callback函數,這個函數在子程序終止的時候被呼叫。
在windows平台上執行.bat和.cmd:
child_process.exec和child_process.execFile的差異可能隨著平台不同而有差異。在Unit/Linux/OSX平台上execFile更有效率,因為他不會產生shell。在windows上,.bat/.cmd在沒有終端的情況下是無法執行的,因此就無法使用execFile(child_process.spawn也無法使用)。在window上,.bat/.cmd可以使用spawn方法,同時指定一個shell選項;或者使用child_process.exec或透過產生一個cmd.exe同時把.bat/.cmd檔傳遞給它作為參數(child_process.exec就是這麼做的)。
const spawn = require('child_process').spawn; const bat = spawn('cmd.exe', ['/c', 'my.bat']);//使用shell方法指定一个shell选项 bat.stdout.on('data', (data) => { console.log(data); }); bat.stderr.on('data', (data) => { console.log(data); }); bat.on('exit', (code) => { console.log(`Child exited with code $[code]`); });或也可以使用以下的方式:
const exec = require('child_process').exec;//产生exec,同时传入.bat文件 exec('my.bat', (err, stdout, stderr) => { if (err) { console.error(err); return; } console.log(stdout); });child_process.exec(command[, options][, callback])
const exec = require('child_process').exec; const child = exec('cat *.js bad_file | wc -l', (error, stdout, stderr) => { console.log(`stdout: ${stdout}`); console.log(`stderr: ${stderr}`); if (error !== null) { console.log(`exec error: ${error}`); } });上面的程式碼產生一個shell,然後使用這個shell執行指令,同時對產生的結果進行快取。其中回呼函數中的error.code屬性表示子進程的exit code,error.signal表示結束這個進程的訊號,任何非0的程式碼表示出現了錯誤。預設的options參數值如下:
{ encoding: 'utf8', timeout: 0, maxBuffer: 200*1024,//stdout和stderr允许的最大的比特数据,超过她子进程就会被杀死 killSignal: 'SIGTERM', cwd: null, env: null }如果timeout非0,那麼父進程就會發送訊號,這個訊號透過killSignal指定,預設是"SIGTERM"來終結子行程,如果子行程超過了timeout指定的時間。注意:和POSIX系統上呼叫exec方法不一樣的是,child_process.exec不會取代目前線程,而是使用一個shell去執行指令
child_process.execFile(file[, args][ , options][, callback])
const execFile = require('child_process').execFile; const child = execFile('node', ['--version'], (error, stdout, stderr) => { if (error) { throw error; } console.log(stdout); });
因为不会产生shell,一些I/O redirection和file globbing这些行为不支持
child_process.fork(modulePath[, args][, options])
其中modulePath表示要在子线程中执行的模块。其中options中的参数silent如果设置为true,那么子进程的stdin, stdout, stderr将会被传递给父进程,如果设置为false那么就会从父进程继承。execArgv默认的值为process.execArgv,execPath表示用于创建子进程的可执行文件。这个fork方法是child_process.spawn的一种特殊用法,用于产生一个Node.js的子进程,和spawn一样返回一个ChildProcess对象。返回的ChildProcess会有一个内置的传输通道用于在子进程和父进程之间传输数据(用ChildProcess的send方法完成)。我们必须注意,产生的Node.js进程和父进程之间是独立的,除了他们之间的IPC传输通道以外。每一个进程有独立的内存,有自己独立的V8引擎。由于产生一个子进程需要其他额外的资源分配,因此产生大量的子进程不被提倡。默认情况下,child_process.fork会使用父进程的process.execPath来产生一个Node.js实例,options中的execPath允许指定一个新的路径。通过指定execPath产生的新的进程和父进程之间通过文件描述符(子进程的环境变量NODE_CHANNEL_FD)来通信。这个文件描述符上的input/output应该是一个JSON对象。和POSIX系统调用fork不一样的是,child_process.fork不会克隆当前的进程
最后,我们来看看子进程和父进程之间是如何通信的:
服务器端的代码:
var http = require('http'); var cp = require('child_process'); var server = http.createServer(function(req, res) { var child = cp.fork(dirname + '/cal.js'); //每个请求都单独生成一个新的子进程 child.on('message', function(m) { res.end(m.result + '\n'); }); //为其指定message事件 var input = parseInt(req.url.substring(1)); //和postMessage很类似,不过这里是通过send方法而不是postMessage方法来完成的 child.send({input : input}); }); server.listen(8000);
子进程的代码:
function fib(n) { if (n < 2) { return 1; } else { return fib(n - 2) + fib(n - 1); } } //接受到send传递过来的参数 process.on('message', function(m) { //console.log(m); //打印{ input: 9 } process.send({result: fib(m.input)}); });
child_process.spawn(command[, args][, options])
其中options对象的stdio参数表示子进程的stdio配置;detached表示让子进程在父进程下独立运行,这个行为与平台有关;shell如果设置为true那么就会在shell中执行命令。这个方法通过指定的command来产生一个新的进程,如果第二个参数没有指定args那么默认就是一个空的数组,第三个参数默认是如下对象,这个参数也可以用于指定额外的参数:
{ cwd: undefined, //产生这个进程的工作目录,默认继承当前的工作目录 env: process.env//这个参数用于指定对于新的进程可见的环境变量,默认是process.env }
其中cwd用于指定子进程产生的工作目录,如果没有指定表示的就是当前工作目录。env用于指定新进程的环境变量,默认为process.env。下面的例子展示了使用ls -lh/usr来获取stdout,stderr以及exit code:
const spawn = require('child_process').spawn; const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.log(`stderr: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code $[code]`); });
下面是一个很详细的运行"ps ax|grep ssh"的例子:
const spawn = require('child_process').spawn; const ps = spawn('ps', ['ax']); const grep = spawn('grep', ['ssh']); ps.stdout.on('data', (data) => { grep.stdin.write(data); }); ps.stderr.on('data', (data) => { console.log(`ps stderr: ${data}`); }); ps.on('close', (code) => { if (code !== 0) { console.log(`ps process exited with code $[code]`); } grep.stdin.end(); }); grep.stdout.on('data', (data) => { console.log(`${data}`); }); grep.stderr.on('data', (data) => { console.log(`grep stderr: ${data}`); }); grep.on('close', (code) => { if (code !== 0) { console.log(`grep process exited with code $[code]`); } });
用下面的例子来检查错误的执行程序:
const spawn = require('child_process').spawn; const child = spawn('bad_command'); child.on('error', (err) => { console.log('Failed to start child process.'); });
options.detached:
在windows上,把这个参数设置为true的话,这时候如果父进程退出了那么子进程还会继续运行,而且子进程有自己的console window。如果把子进程设置了这个为true,那么就不能设置为false了。在非window平台上,如果把这个设置为true,子进程就会成为进程组合和session的leader,这时候子进程在父进程退出以后会继续执行,不管子进程是否detached。可以参见setsid(2)。
默认情况下,父进程会等待detached子进程,然后退出。要阻止父进程等待一个指定的子进程可以使用child.unref方法,这个方法会让父进程的事件循环不包括子进程,这时候允许父进程独立退出,除非在父进程和子进程之间有一个IPC通道。下面是一个detach长期运行的进程然后把它的输出导向到一个文件:
const fs = require('fs'); const spawn = require('child_process').spawn; const out = fs.openSync('./out.log', 'a'); const err = fs.openSync('./out.log', 'a'); const child = spawn('prg', [], { detached: true,//依赖于父进程 stdio: [ 'ignore', out, err ] }); child.unref();//允许父进程单独退出,不用等待子进程
当使用了detached选项去产生一个长期执行的进程,这时候如果父进程退出了那么子进程就不会继续执行了,除非指定了一个stdio配置(不和父进程之间有联系)。如果父进程的stdio是继承的,那么子进程依然会和控制终端之间保持关系。
options.stdio
这个选项用于配置父进程和子进程之间的管道。默认情况下,子进程的stdin,stdout,stderr导向了ChildProcess这个对象的child.stdin,child.stdout,child.stderr流,这和设置stdio为['pipe','pipe','pipe']是一样的。stdio可以是下面的任何一个字符串:
'pipe':相当于['pipe','pipe','pipe'],为默认选项
'ignore':相当于['ignore','ignore','ignore']
'inherit':相当于[process.stdin,process.stdout,process.stderr]或者[0,1,2]
一般情况下,stdio是一个数组,每一个选项对应于子进程的fd。其中0,1,2分别对应于stdin,stdout,stderr。如果还设置了多于的fds那么就会用于创建父进程和子进程之间的额外的管道,可以是下面的任何一个值:
'pipe':为子进程和父进程之间创建一个管道。父进程管道的末端会作为child_process对象的ChildProcess.stdio[fd]而存在。fds0-2创建的管道在ChildProcess.stdin,ChildProcess.stdout,ChildProcess.stderr也是存在的
'ipc':用于创建IPC通道用于在父进程和子进程之间传输消息或者文件描述符。ChildProcess对象最多有一个IPC stdio文件描述符,使用这个配置可以启用ChildProcess的send方法,如果父进程在文件描述符里面写入了JSON对象,那么ChildProcess.on("message")事件就会在父进程上触发。如果子进程是Node.js进程,那么ipc配置就会启用子进程的process.send(), process.disconnect(), process.on('disconnect'), and process.on('message')方法。
'ignore':让Node.js子进程忽视文件描述符。因为Node.js总是会为子进程开启fds0-2,设置为ignore就会导致Node.js去开启/dev/null,同时把这个值设置到子进程的fd上面。
'strem':和子进程之间共享一个可读或者可写流,比如file,socket,pipe。这个stream的文件描述符和子进程的文件描述符fd是重复的。注意:流必须有自己的文件描述符
正整数:表示父进程的打开的文件描述符。和stream对象可以共享一样,这个文件描述符在父子进程之间也是共享的
null/undefined:使用默认值。stdio的fds0,1,2管道被创建(stdin,stdout,stderr)。对于fd3或者fdn,默认为'ignore'
const spawn = require('child_process').spawn; // Child will use parent's stdios //使用父进程的stdios spawn('prg', [], { stdio: 'inherit' }); //产生一个共享process.stderr的子进程 // Spawn child sharing only stderr spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] }); // Open an extra fd=4, to interact with programs presenting a // startd-style interface. spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });
注意:当子进程和父进程之间建立了IPC通道,同时子进程为Node.js进程,这时候开启的具有IPC通道的子进程(使用unref)直到子进程注册了一个disconnect的事件处理句柄process.on('disconnect'),这样就会允许子进程正常退出而不会由于IPC通道的打开而持续运行。
Class: ChildProcess
这个类的实例是一个EventEmitters,用于代表产生的子进程。这个类的实例不能直接创建,必须使用 child_process.spawn(), child_process.exec(), child_process.execFile(), or child_process.fork()来完成
'close'事件:
其中code表示子进程退出的时候的退出码;signal表示终止子进程发出的信号;这个事件当子进程的stdio stream被关闭的时候触发,和exit事件的区别是多个进程可能共享同一个stdio streams!(所以一个进程退出了也就是exit被触发了,这时候close可能不会触发)
'exit'事件:
其中code表示子进程退出的时候的退出码;signal表示终止子进程发出的信号。这个事件当子进程结束的时候触发,如果进程退出了那么code表示进程退出的exit code,否则没有退出就是null。如果进程是由于收到一个信号而终止的,那么signal就是这个信号,是一个string,默认为null。
注意:如果exit事件被触发了,子进程的stdio stream可能还是打开的;Node.js为SUGUBT,SIGTERM创建信号处理器,而且Node.js进程在收到信号的时候不会马上停止。Node.js会进行一系列的清理工作,然后才re-raise handled signal。见waitpid(2)
'disconnect'事件:
在子进程或者父进程中调用ChildProcess.disconnect()方法的时候会触发。这时候就不能再发送和接受信息了,这是ChildProcess.connected就是false了
'error'事件:
当进程无法产生的时候,进程无法杀死的时候,为子进程发送消息失败的时候就会触发。注意:当产生错误的时候exit事件可能会也可能不会触发。如果你同时监听了exit和error事件,那么就要注意是否会无意中多次调用事件处理函数
'message'事件:
message参数表示一个解析后的JSON对象或者初始值;sendHandle可以是一个net.Socket或者net.Server对象或者undefined。当子进程调用process.send时候触发
child.connected:
调用了disconnect方法后就会是false。表示是否可以在父进程和子进程之间发送和接受数据,当值为false就不能发送数据了
child.disconnect()
关闭子进程和父进程之间的IPC通道,这时候子进程可以正常退出如果没有其他的连接使得他保持活动。这时候父进程的child.connected和子进程的process.connected就会设置为false,这时候不能传输数据了。disconnect事件当进程没有消息接收到的时候被触发,当调用child.disconnect时候会立即触发。注意:当子进程为Node.js实例的时候如child_process.fork,这时候process.disconnect方法就会在子进程中调用然后关闭IPC通道。
child.kill([signal])
为子进程传入消息,如果没有指定参数那么就会发送SIGTERM信号,可以参见signal(7)来查看一系列信号
const spawn = require('child_process').spawn; const grep = spawn('grep', ['ssh']); grep.on('close', (code, signal) => { console.log( `child process terminated due to receipt of signal ${signal}`); }); // Send SIGHUP to process grep.kill('SIGHUP');
ChildProcess对象在无法传输信号的时候会触发error事件。为一个已经退出的子进程发送信号虽然无法报错但是可能导致无法预料的结果。特别的,如果这个PID已经被分配给另外一个进程那么这时候也会导致无法预料的结果。
child.pid:
返回进程的PID值
const spawn = require('child_process').spawn; const grep = spawn('grep', ['ssh']); console.log(`Spawned child pid: ${grep.pid}`); grep.stdin.end();//通过grep.stdin.end结束
child.send(message[, sendHandle][, callback])
当父子进程之间有了IPC通道,child.send就会为子进程发送消息,当子进程为Node.js实例,那么可以用process.on('message')事件接收
父进程为:
const cp = require('child_process'); const n = cp.fork(`${dirname}/sub.js`); n.on('message', (m) => { console.log('PARENT got message:', m); }); n.send({ hello: 'world' });
子进程为:
process.on('message', (m) => { console.log('CHILD got message:', m); }); process.send({ foo: 'bar' });
子进程使用process.send方法为父进程发送消息。有一个特例,发送{cmd: 'NODE_foo'}。当一个消息在他的cmd属性中包含一个NODE_前缀会被看做使用Node.js核心(被Node.js保留)。这时候不会触发子进程的process.on('message')。而是使用process.on('internalMessage')事件,同时会被Node.js内部消费,一般不要使用这个方法。sendHandle这个用于给子进程传入一个TCP Server或者一个socket,为process.on('message')回调的第二个参数接受。callback当消息已经发送,但是子进程还没有接收到的时候触发,这个函数只有一个参数成功为null否则为Error对象。如果没有指定callback同时消息也不能发送ChildProcess就会触发error事件。当子进程已经退出就会出现这个情况。child.send返回false如果父子进程通道已经关闭,或者积压的没有传输的数据超过一定的限度,否则这个方法返回true。这个callback方法可以用于实现流控制:
下面是发送一个Server的例子:
const child = require('child_process').fork('child.js'); // Open up the server object and send the handle. const server = require('net').createServer(); server.on('connection', (socket) => { socket.end('handled by parent'); }); server.listen(1337, () => { child.send('server', server); });
子进程接受这个消息:
process.on('message', (m, server) => { if (m === 'server') { server.on('connection', (socket) => { socket.end('handled by child'); }); } });
这时候server就被子进程和父进程共享了,一些连接可以被父进程处理,一些被子进程处理。上面的例子如果使用dgram那么就应该监听message事件而不是connection,使用server.bind而不是server.listen,但是当前只在UNIX平台上可行。
下面的例子展示发送一个socket对象(产生两个子进程,处理normal和special优先级):
父进程为:
const normal = require('child_process').fork('child.js', ['normal']); const special = require('child_process').fork('child.js', ['special']); // Open up the server and send sockets to child const server = require('net').createServer(); server.on('connection', (socket) => { // If this is special priority if (socket.remoteAddress === '74.125.127.100') { special.send('socket', socket); return; } // This is normal priority normal.send('socket', socket); }); server.listen(1337);
子进程为:
process.on('message', (m, socket) => { if (m === 'socket') { socket.end(`Request handled with ${process.argv[2]} priority`); } });
当socket被发送到子进程的时候那么父进程已经无法追踪这个socket什么时候被销毁的。这时候.connections属性就会成为null,因此我们建议不要使用.maxConnections。注意:这个方法在内部JSON.stringify去序列化消息
child.stderr:
一个流对象,是一个可读流表示子进程的stderr。他是child.stdio[2]的别名,两者表示同样的值。如果子进程是通过stdio[2]产生的,设置的不是pipe那么值就是undefined。
child.stdin:
一个可写的流对象。注意:如果子进程等待读取输入,那么子进程会一直等到流调用了end方法来关闭的时候才会继续读取。如果子进程通过stdio[0]产生,同时不是设置的pipe那么值就是undefined。child.stdin是child.stdio[0]的别名,表示同样的值。
const spawn = require('child_process').spawn; const grep = spawn('grep', ['ssh']); console.log(`Spawned child pid: ${grep.pid}`); grep.stdin.end();//通过grep.stdin.end结束
child.stdio:
一个子进程管道的稀疏数组,是 child_process.spawn()函数的stdio选项,同时这个值被设置为pipe。child.stdio[0], child.stdio[1], 和 child.stdio[2]也可以通过child.stdin, child.stdout, 和 child.stderr访问。下面的例子中只有子进程的fd1(也就是stdout)被设置为管道,因此只有父进程的child.stdio[1]是一个流,其他的数组中对象都是null:
const assert = require('assert'); const fs = require('fs'); const child_process = require('child_process'); const child = child_process.spawn('ls', { stdio: [ 0, // Use parents stdin for child 'pipe', // Pipe child's stdout to parent fs.openSync('err.out', 'w') // Direct child's stderr to a file ] }); assert.equal(child.stdio[0], null); assert.equal(child.stdio[0], child.stdin); assert(child.stdout); assert.equal(child.stdio[1], child.stdout); assert.equal(child.stdio[2], null); assert.equal(child.stdio[2], child.stderr);
child.stdout:
一个可读流,代表子进程的stdout。如果子进程产生的时候吧stdio[1]设置为除了pipe以外的任何数,那么值就是undefined。其值和child.stdio[1]一样
以上是詳解從Node.js的child_process模組來學習父子進程之間的通訊的範例程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1
好用且免費的程式碼編輯器

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),