ノードは I/O 操作を効率的に処理するように設計されていますが、一部の種類のプログラムはこのモデルに適していないことに注意してください。たとえば、Node を使用して CPU 集中型のタスクを処理する予定の場合、イベント ループが詰まり、プログラムの応答性が低下する可能性があります。別の方法は、CPU を大量に使用するタスクを別のプロセスにオフロードして、イベント ループを解放することです。ノードを使用すると、プロセスを生成し、新しいプロセスをその親の子にすることができます。 Node では、子プロセスは親プロセスと双方向で通信でき、ある程度、親プロセスも子プロセスを監視および管理できます。
サブプロセスを使用する必要があるもう 1 つの状況は、単純に外部コマンドを実行して、Node にコマンドの戻り値を取得させたい場合です。たとえば、Node.js で直接実行できない UNIX コマンド、スクリプト、またはその他のコマンドを実行できます。
この章では、外部コマンドの実行方法、子プロセスの作成と通信、子プロセスの終了方法を説明します。重要なのは、ノード プロセスの外で一連のタスクを完了する方法を理解できるようにすることです。
外部コマンドの実行
外部シェル コマンドまたは実行可能ファイルを実行する必要がある場合は、child_process モジュールを使用して、次のようにインポートできます。
exec(コマンド,コールバック);
//翻訳者注: Windows を使用している場合は、dir などの Windows コマンドに変更できますが、これについては再度説明しません
});
エラーが発生した場合、最初のパラメーターは Error クラスのインスタンスになります。最初のパラメーターにエラーが含まれていない場合、2 番目のパラメーター stdout にはコマンドの標準出力が含まれます。最後のパラメータには、コマンドに関連するエラー出力が含まれます。
リスト 8-1 は、外部コマンドを実行するより複雑な例を示しています
リスト 8-1: 外部コマンドの実行 (ソース コード: Chapter8/01_external_command.js)
4 行目では、exec の最初のパラメータとして「cat *.js | wc -l」を渡します。シェルでコマンドを使用している限り、他のコマンドを試すこともできます。
次に、エラーが発生したとき、または子プロセスが終了したときに呼び出されるコールバック関数を 2 番目のパラメータとして渡します。
コールバック関数の前に、次のような構成オプションを含む 3 番目のオプションのパラメータを渡すこともできます。
タイムアウト: 1000、
killSignal: 「SIGKILL」
};
//…
});
1.cwd - 現在のディレクトリ。現在の作業ディレクトリを指定できます。
2.encoding - 子プロセスの出力コンテンツのエンコード形式。デフォルト値は「utf8」で、UTF-8 エンコードです。子プロセスの出力が utf8 ではない場合、このパラメータを使用して設定できます。サポートされているエンコード形式は次のとおりです。
」を参照してください。 1.timeout - コマンド実行タイムアウト (ミリ秒単位)。デフォルトは 0 で、子プロセスが終了するまで制限なしを意味します。
2.maxBuffer - stdout ストリームと stderr ストリームによって出力できる最大バイト数を指定します。最大値に達すると、子プロセスが強制終了されます。デフォルト値は 200*1024 です。
3.killSignal - タイムアウトするか、出力バッファーが最大サイズに達したときに、子プロセスに送信される終了シグナル。デフォルト値は「SIGTERM」で、子プロセスに終了シグナルを送信します。この順序立てたアプローチは通常、プロセスを終了するために使用されます。 SIGTERM シグナルを使用する場合、プロセスはシグナルを処理したり、シグナルを受信した後にシグナル プロセッサのデフォルトの動作をオーバーライドしたりできます。ターゲット プロセスで必要な場合は、他のシグナル (SIGUSR1 など) を同時にターゲット プロセスに渡すことができます。 SIGKILL シグナルの送信を選択することもできます。このシグナルはオペレーティング システムによって処理され、子プロセスを即時に強制的に終了します。この場合、子プロセスのクリーンアップ操作は実行されません。
プロセスの終了をさらに制御したい場合は、後で紹介する child_process.spawn コマンドを使用できます。
1.evn - 子プロセスに渡される環境変数を指定します。デフォルトは null です。これは、子プロセスが作成される前にすべての親プロセスの環境変数を継承することを意味します。
注: killSignal オプションを使用すると、ターゲット プロセスに文字列の形式でシグナルを送信できます。シグナルは、Node 内の文字列の形式で存在します。 以下は、UNIX シグナルと対応するデフォルトの操作のリストです。
子プロセスに拡張可能な親環境変数のセットを提供することもできます。 process.env オブジェクトを直接変更すると、Node プロセス内のすべてのモジュールの環境変数が変更されるため、多くの問題が発生します。別の方法は、新しいオブジェクトを作成して process.env 内のすべてのパラメータをコピーすることです。例 8-2: を参照してください。
リスト 8-2: パラメーター化された環境変数を使用してコマンドを実行する (ソース コード: Chapter8/02_env_vars_augment.js)
//process.env とカスタム変数を使用してコマンドを実行します
exec(‘ls –la’,{env: envCopy}, function(err,stdout,stderr){
console.log(‘stdout:’, stdout);
console.log(‘stderr:’,stderr);
}
上記の例では、環境変数を保存するために envCopy 変数を作成します。まず、Node プロセスの環境変数を process.env からコピーし、次に変更が必要ないくつかの環境変数を追加または置換し、最後に envCopy を環境として使用します。 . 変数引数が exec 関数に渡され、外部コマンドが実行されます。
環境変数はオペレーティング システムを通じてプロセス間で受け渡され、あらゆる種類の環境変数値が文字列の形式で子プロセスに届くことに注意してください。たとえば、親プロセスに環境変数として数値 123 が含まれている場合、子プロセスは文字列として「123」を受け取ります。
次の例では、同じディレクトリに 2 つのノード スクリプト (parent.js と child.js) を作成します。最初のスクリプトは 2 つ目のファイルを呼び出します。
リスト 8-3: 親プロセスが環境変数を設定する (chapter8/03_environment_number_parent.js)
exec('node child.js', {env: {number: 123}}, function(err, stdout, stderr) {
if (err) { エラーをスローする }
console.log('stdout:n', stdout);
console.log('stderr:n', stderr);
});
このコードをparent.jsに保存します。以下は子プロセスのソースコードであり、child.jsに保存します(例8-4を参照)。
例 8-4: 子プロセスの環境変数解析 (chapter8/04_environment_number_child.js)
console.log(typeof(number)); // → "文字列"
数値 = parseInt(数値, 10);
console.log(typeof(number)) // → "数値"
このファイルを child.js として保存した後、このディレクトリで次のコマンドを実行できます:
次の出力が表示されます:
文字列
番号
stderr:
ご覧のとおり、親プロセスは数値環境変数を渡しますが、子プロセスはそれを文字列として受け取ります (出力の 2 行目を参照) 3 行目で文字列を数値に解析します。
子プロセスの生成
ご覧のとおり、child_process.exec() 関数を使用して外部プロセスを開始し、プロセスの終了時にコールバック関数を呼び出すことができます。これは非常に簡単に使用できますが、いくつかの欠点もあります。
1. コマンドラインパラメータと環境変数を使用することに加えて、exec() は子プロセスと通信できません
2. 子プロセスの出力はキャッシュされるため、メモリ不足になる可能性があるためストリーミングできません
幸いなことに、Node の child_process モジュールを使用すると、子プロセスの開始、停止、その他の一般的な操作をより詳細に制御できます。アプリケーションで新しい子プロセスを開始できます。Node は、親プロセスと子プロセスが相互に文字列データを送受信できるようにする双方向通信チャネルを提供します。親プロセスは、子プロセスに対していくつかの管理操作を実行し、子プロセスにシグナルを送信し、子プロセスを強制的に終了することもできます。
子プロセスの作成
child_process.spawn 関数を使用して新しい子プロセスを作成できます。例 8-5 を参照してください。
例 8-5: 子プロセスを生成します。 (chapter8/05_spawning_child.js)
var spawn = require('child_process').spawn;
// 「tail -f /var/log/system.log」コマンドを実行するための子プロセスを生成します
var child = spawn('tail', ['-f', '/var/log/system.log']);
上記のコードは、tail コマンドを実行するためのサブプロセスを生成し、パラメーターとして「-f」と「/bar/log/system.log」を受け取ります。 tail コマンドは、/var/log/system.og ファイル (存在する場合) を監視し、追加された新しいデータを標準出力ストリームに出力します。 spawn 関数は、実際のプロセスのアクセス インターフェイスをカプセル化するポインタ オブジェクトである ChildProcess オブジェクトを返します。この例では、この新しい記述子を child という変数に割り当てます。
子プロセスからのデータをリッスン
stdout 属性を含む子プロセス ハンドルは、子プロセスの標準出力 stdout をストリーム オブジェクトとして使用します。これにより、データ ブロックが利用可能になるたびに対応するコールバックが行われるように、データ イベントをこのストリーム オブジェクトにバインドできます。関数については、以下の例を参照してください:
child.stdout.on('data',function(data){
console.log(‘末尾出力: ‘ データ);
});
子プロセスがデータを標準出力に出力するたびに、親プロセスに通知され、データがコンソールに出力されます。
標準出力に加えて、プロセスには別のデフォルトの出力ストリームがあります。標準エラー ストリームは、通常、エラー情報を出力するために使用されます。
この例では、/var/log/system.log ファイルが存在しない場合、末尾プロセスは次のようなメッセージを出力します: 「/var/log/system.log: そのようなファイルまたはディレクトリはありません」 、stderrストリームを監視することで、そのようなエラーが発生したときに親プロセスに通知されます。
親プロセスは次のように標準エラー ストリームをリッスンできます:
console.log('末尾のエラー出力:', data);
});
stderr 属性も、stdout と同様に読み取り専用ストリームであり、子プロセスがデータを標準エラー ストリームに出力するたびに、親プロセスに通知され、データが出力されます。
子プロセスにデータを送信します
親プロセスは、子プロセスの出力ストリームからデータを受信するだけでなく、childPoces.stdin 属性を通じて子プロセスの標準入力にデータを書き込み、子プロセスとの間でデータを送受信することもできます。
子プロセスは、 process.stdin 読み取り専用ストリームを通じて標準入力データを監視できますが、標準入力ストリームはデフォルトで一時停止状態にあるため、最初に再開する必要があることに注意してください。
例 8-6 では、次の関数を含むプログラムを作成します。
1. 1 アプリケーション: 標準入力から整数を受け取り、それらを加算し、加算結果を標準出力ストリームに出力できるシンプルなアプリケーションです。このアプリケーションは、単純なコンピューティング サービスとして、特定の作業を実行できる外部サービスとしてノード プロセスをシミュレートします。
2. 1 つのアプリケーションのクライアントをテストし、ランダムな整数を送信し、結果を出力します。ノード プロセスが子プロセスを生成し、その子プロセスに特定のタスクを実行させる方法を示すために使用されます。
以下の例 8-6 のコードを使用して、plus_one.js という名前のファイルを作成します。
例 8-6: 1 つのアプリケーション (chapter8/06_plus_one.js)
上記のコードでは、stdin 標準入力ストリームからのデータを待機し、データが利用可能になると、それが整数であると仮定して整数変数に解析し、1 を加算して結果を標準出力ストリームに出力します。 。
次のコマンドでこのプログラムを実行できます:
実行後、プログラムは入力待ちを開始します。整数を入力して Enter を押すと、画面に 1 が加算された数値が表示されます。
Ctrl+C を押すとプログラムを終了できます。
テストクライアント
次に、前の「1 つのアプリケーション」によって提供されるコンピューティング サービスを使用するためのノード プロセスを作成する必要があります。
まず、plus_one_test.js という名前のファイルを作成します。その内容を例 8-7 に示します。
例 8-7: アプリケーション 1 のテスト (chapter8/07_plus_one_test.js)
1 行目から 4 行目までは「1 つのアプリケーション」を実行するためのサブプロセスが開始され、setInterval 関数を使用して 1 秒に 1 回以下の操作が実行されます。
1.. 10000 未満の新しい乱数を作成します
2. この数値を文字列として子プロセス
に渡します。
3. 子プロセスが文字列
で応答するのを待ちます。
4. 一度に 1 つの数値の計算結果だけを受け取りたいため、child.stdout.on の代わりに child.stdout.once を使用する必要があります。後者を使用すると、データイベントのコールバック関数が1秒ごとに登録され、子プロセスのstdoutがデータを受信した際に登録された各コールバック関数が実行されるため、同じ計算結果が出力されることがわかります。多くの場合、この動作は明らかに間違っています。
子プロセスが終了したときに通知を受け取る
子プロセスが終了すると、exit イベントがトリガーされます。例 8-8 は、それをリッスンする方法を示しています:例 8-8: 子プロセスの終了イベントをリッスンする (chapter8/09_listen_child_exit.js)
child.on('exit', function(code) {
console.log('子プロセスはコードで終了しました ' code);
});
例 8-9: 子プロセスの終了コードを取得する (chapter8/10_child_exit_code.js)
のように、対応するシグナル コードが 2 番目のパラメーターとしてコールバック関数に渡されます。
リスト 8-10: 子プロセスの終了シグナルを取得する (chapter8/11_child_exit_signal.js)
この例では、子プロセスが 10 秒間のスリープ操作を実行するために開始されますが、10 秒前に SIGKILL シグナルが子プロセスに送信され、次の出力が得られます。
シグナルを送信してプロセスを強制終了
このパートでは、シグナルを使用して子プロセスを管理する方法を学びます。シグナルは、親プロセスが子プロセスと通信したり、子プロセスを強制終了したりするための簡単な方法です。
異なるシグナル コードは異なる意味を表します。シグナルは数多くありますが、最も一般的なもののいくつかはプロセスを強制終了するために使用されます。プロセスが処理方法を知らないシグナルを受信した場合、プログラムは異常中断されます。一部の信号は子プロセスによって処理されますが、その他の信号はオペレーティング システムによってのみ処理されます。
通常、child.kill メソッドを使用して子プロセスにシグナルを送信できます。デフォルトでは SIGTERM シグナルが送信されます。
概要
この章では、child_process.exec メソッドを使用して外部コマンドを実行する方法を学習しました。このメソッドはコマンド ライン パラメーターを使用せず、環境変数を定義することでパラメーターを子プロセスに渡します。child_process.spawn メソッドを呼び出して子プロセスを生成することで外部コマンドを呼び出す方法も学習しました。この方法では、入力ストリームと出力ストリームを使用して子プロセスと通信したり、シグナルを使用して子プロセスと通信したりできます。子プロセスを強制終了します。