node.js中process進程的概念和child_process子進程模塊的使用


進程,你可以把它理解成一個正在運行的程序。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() 的封裝。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM