進程,你可以把它理解成一個正在運行的程序。node.js中每個應用程序都是進程類的實例對象。
node.js中有一個 process 全局對象,通過它我們可以獲取,運行該程序的用戶,環境變量等信息。
一、process 對象
console.log('可執行文件絕對路徑', process.execPath); console.log('版本號', process.version); console.log('依賴庫的版本號', process.versions); console.log('運行平台', process.platform); console.log('標准輸入流', process.stdin); console.log('標准輸出流', process.stdout); console.log('標准錯誤流', process.stderr); console.log('命令行參數數組', process.argv); console.log('系統環境變量', process.env); console.log('進程ID', process.pid); console.log('標題', process.title); console.log('處理器架構', process.arch);
通過 memoryUsage() 查看內存使用量:
console.log(process.memoryUsage());
rss 表示進程占用的內存,包括堆,棧,代碼段。
heapTotal 表示堆占用的內存。
heapUsed 表示堆使用的部分。
external 表示外部使用的部分,C++對象占用的。
對象,字符串,閉包存放於堆內存,變量存放於棧內存,js源代碼存放於代碼段。
console.log(process.memoryUsage()); let buf = Buffer.alloc(1024 * 1024 * 1024); console.log(process.memoryUsage());
當我們通過Buffer創建一個足夠大的變量時,這時只能借助於外部內存,使用C++去完成。node.js能夠使用的內存上限是1.7G。
使用 chdir() 修改程序當前的工作目錄,通過 cwd() 獲取當前工作目錄。
console.log(process.cwd()); //修改程序當前的工作目錄 process.chdir('../'); console.log(process.cwd());
通過 exit() 來結束進程
process.exit(0);
調用 exit() 結束進程時,會觸發 'exit' 事件。
process.on('exit', function () { console.log('程序退出了'); }); process.exit(0);
通過 kill() 給指定進程發送信號
SIGINT 程序終止信號,當用戶按下ctrl+c時發出,將通知進程終止。
SIGTERM 程序結束信號,通知程序正常退出,kill()方法默認就是這個信號。
process.kill(process.pid, 'SIGINT');
通過 uptime() 返回程序運行的時間
console.log(process.uptime());
通過 hrtime() 計算代碼段運行時間,hrtime() 返回一個數組,第一個表示秒,第二個表示納秒
let start = process.hrtime(); let sum = 0; for (i = 0; i < 1000000000; i++) { sum += i; } let end = process.hrtime(start); console.log('耗時 : ', end[0], '秒');
當程序拋出一個沒有被捕獲的異常時,觸發 'uncaughtException' 事件。
process.on('uncaughtException', function (err) { console.log('捕獲了一個未被處理的異常'); console.log(err); }); //調用一個未定義的函數 test();
進程接收到一個信號時,會觸發信號事件,我們可以監聽到該事件。
//讓標准輸入流處於流動模式,讓程序無法退出 process.stdin.resume(); process.on('SIGINT', function () { console.log('程序退出'); process.exit(0); }); process.on('SIGTERM', function () { console.log('程序結束'); });
二、子進程模塊child_process的使用
我們都知道node.js是單線程的,如果某一個操作需要消耗大量資源和時間,會導致程序整體性能下降。
我們可以創建子進程,讓子進程去跑那些費時費力的操作,而主線程該干嘛干嘛。
子進程間可以共享內存,通過互相通信來完成數據的交換。
1、通過 spawn() 創建子進程
const {spawn} = require('child_process'); //參數一表示,要執行的命令 //參數二表示,運行該命令的參數 //參數三表示,創建子進程的配置 let cp1 = spawn('node', ['1.js'], { //cwd表示當前子進程的工作目錄 cwd: process.cwd(), //子進程的環境變量 env: process.env, //子進程的標准輸入,標准輸出,錯誤,的配置 //pipe表示,父進程與子進程間建立管道,父進程可以訪問子進程對應的輸入,輸出,和錯誤 //ipc表示,父進程與子進程間建立一個專門用來傳遞消息的IPC通道,子進程調用send()方法向子進程發送消息,並觸發'message'事件 //ignore表示,忽略子進程的標准輸入,標准輸出,錯誤。 //inherit表示,子進程共享父進程的標准輸入,標准輸出,錯誤。 //stream表示,父進程與子進程共享一個流,比如文件流或socket。 //正整數表示,父進程打開的文件描述符,與子進程共享,比如文件的fd。類似stream流對象共享。 //null或undefined表示,父進程與子進程間創建管道 stdio: ['pipe', process.stdout, 'pipe'], //子進程是否獨立於父進程運行 detached: false });
1.js的代碼:
console.log('hello');
運行代碼后,我們可以看到子進程的 'hello',出現在了父進程的標准輸出上。因為 stdio 的配置,我們讓子進程與父進程共享標准輸出。
spawn() 會返回一個子進程對象,我們可以監聽該對象的一些事件。
const {spawn} = require('child_process'); let cp1 = spawn('node', ['1.js'], { cwd: process.cwd(), env: process.env, stdio: ['pipe', process.stdout, 'pipe'], detached: false }); //子進程所有輸入/輸出終止時,會觸發子進程的 'close' 事件 cp1.on('close', function (code, signal) { //當父進程關閉子進程時,signal表示父進程發送給子進程的信號名稱 console.log('子進程關閉了', code, signal); }); //子進程退出時,會觸發 'exit' 事件 //注意,子進程退出,子進程的輸入/輸出有可能並未關閉。因為輸入/輸出有可能多個進程共享。 cp1.on('exit', function (code, signal) { console.log('子進程退出', code, signal); }); //子進程出錯時,觸發 cp1.on('error', function (err) { console.log(err); });
注意,stdio 設置成 pipe ,是把子進程的stdin,stdout,stderr導向了 spawn() 返回的子進程對象的stdin,stdout,stderr。
然后父進程就可以通過子進程對象訪問stdin,stdout,stderr。
const {spawn} = require('child_process'); let cp1 = spawn('node', ['1.js'], { cwd: process.cwd(), env: process.env, stdio: ['pipe', 'pipe', 'pipe'], detached: false }); //監聽子進程標准輸入,輸出,錯誤的數據。 cp1.stdin.on('data', function (data) { console.log(data.toString()); }); cp1.stdout.on('data', function (data) { console.log(data.toString()); }); cp1.stderr.on('data', function (data) { console.log(data.toString()); });
1.js的代碼:
//往子進程標准輸出中寫入數據 console.log('我是標准輸出'); //往子進程錯誤中寫入數據 console.error('我是一個錯誤'); //往子進程標准輸入中寫入數據 process.stdin.write('我是標准輸入');
當我們設置 stdio 為 ipc 時,會創建一個父進程與子進程傳遞消息的IPC通道。
const {spawn} = require('child_process'); let cp1 = spawn('node', ['1.js'], { cwd: process.cwd(), env: process.env, //注意這里,子進程只能有一個IPC通道 stdio: ['pipe', 'ipc', 'pipe'], detached: false }); //注意這里要用子進程對象進行監聽 //監聽有沒有消息 cp1.on('message', function (data) { console.log('子進程發送的 : ', data.toString()); }); cp1.send('你好,子進程');
1.js的代碼:
process.on('message', function (data) { console.log('父進程發送的 : ', data.toString()); }); //向父進程發送消息 process.send('你好,父進程');
默認情況下,只有子進程全部退出了,父進程才能退出。我們希望父進程退出了,子進程仍然獨立運行。可以通過設置 detached 為 true。
默認情況下,父進程會等待所有子程退出后,才退出。通過使用 unref() 讓父進程無需等待子進程就可直接退出。
const {spawn} = require('child_process'); const fs = require('fs'); let fd = fs.openSync('./1.txt', 'w', 0o666); let cp1 = spawn('node', ['1.js'], { cwd: process.cwd(), //注意這里,把不需要的設置為ignore,不然主進程仍然會阻塞等待子進程 stdio: ['ignore', fd, 'ignore'], detached: true }); cp1.on('error', function (err) { console.log(err); }); //解綁子進程,讓父進程不用等待子進程 cp1.unref();
1.js的代碼:
let i = 0; let timer = setInterval(function () { if (i > 20) { clearInterval(timer); } process.stdout.write('寫入數據' + i + '\r\n'); i++; }, 1000);
2、通過 fork() 創建子進程
fork() 是 spawn() 的特殊情況,用於創建新的進程,默認建立一個IPC通信通道,允許父進程與子進程進行消息傳遞。
fork() 返回一個子進程對象,子進程輸入/輸出操作執行完畢后,父進程不退出,子進程不會自動退出,需調用 exit() 顯式退出。
const {fork} = require('child_process'); //參數一表示,運行的模塊 //參數二表示,參數列表 //參數三表示,創建子進程的配置 let cp1 = fork('2.js', ['1', '2', '3'], { //子進程的工作目錄 cwd: process.cwd(), //子進程的環境變量 env: process.env, //運行模塊的可執行文件 execPath: process.execPath, //傳遞給可執行文件的參數列表 execArgv: process.execArgv, //為false表示父進程與子進程共享標准(輸入/輸出),為true時不共享。 silent: false }); cp1.on('error', function (err) { console.log(err); });
2.js的代碼:
for (let i = 0; i < process.argv.length; i++) { process.stdout.write(process.argv[i] + '\r\n'); }
父進程與子進程間,通過 send() 和 'message'事件來傳遞消息。
const {fork} = require('child_process'); let cp1 = fork('2.js', [], { cwd: process.cwd(), env: process.env, silent: false }); //接收消息 cp1.on('message', function (data) { console.log('父進程收到 : ', JSON.stringify(data)); process.exit(0); }); //發送消息 cp1.send({name: '你好子進程'});
2.js的代碼:
process.on('message', function (data) { console.log('子進程收到 : ', JSON.stringify(data)); process.send({name: '你好父進程'}); });
3、通過exec() 創建子進程
exec() 可以開啟一個子進程運行命令,並緩存子進程的輸出結果。
const {exec} = require('child_process'); //參數一表示,要運行的命令 //參數二表示,配置選項 //參數三表示,進程終止時的回調 exec('dir', { //子進程的工作目錄 cwd: process.cwd(), //子進程的環境變量 env: process.env, //輸出的編碼 encoding: 'utf8', //超時時間 timeout: 60 * 1000, //緩存stdout,stderr最大的字節數 maxBuffer: 1024 * 1024, //關閉子進程的信號 killSignal: 'SIGTERM' }, function (err, stdout, stderr) { console.log(stdout.toString()); });
4、通過 execFile() 創建子進程
使用 execFile() 開啟一個運行可執行文件的子進程。
const {execFile} = require('child_process'); //參數一表示,可執行文件的名稱或路徑 //參數二表示,參數列表 //參數三表示,配置選項 //參數四表示,進程終止時的回調 let cp1 = execFile('node', ['3.js', '1', '2', '3'], { //子進程的工作目錄 cwd: process.cwd(), //子進程的環境變量 env: process.env, //輸出的編碼 encoding: 'utf8', //超時時間 timeout: 60 * 1000, //緩存stdout,stderr最大的字節數 maxBuffer: 1024 * 1024, //關閉子進程的信號 killSignal: 'SIGTERM' }, function (err, stdout, stderr) { if (err) { console.log(err); process.exit(); } console.log('子進程的輸出 : ', stdout.toString()); }); cp1.on('error', function (err) { console.log(err); });
3.js的代碼:
process.argv.forEach(function (value) { process.stdout.write(value + '\r\n'); });
fork(),exec(),execFile() 都是基於 spawn() 的封裝。