ノードは、I/O操作を効率的に処理するように設計されていますが、一部のタイプのプログラムはこのモードには適していないことを知っておく必要があります。たとえば、ノードを使用してCPU集約型タスクを処理する予定の場合、イベントループをブロックしてプログラムの応答を減らすことができます。別の方法は、CPU集約型タスクに個別のプロセスを処理するために割り当て、イベントループを解放することです。ノードを使用すると、プロセスを生み出し、この新しいプロセスを親プロセスの子供として使用できます。ノードでは、子プロセスは親プロセスと双方向で通信でき、ある程度、親プロセスは子プロセスを監視および管理することもできます。
子プロセスを使用する必要がある別のケースは、外部コマンドを単純に実行し、ノードにコマンドの返品値を取得する場合です。たとえば、ノードで直接実行できないUNIXコマンド、スクリプト、またはその他のコマンドを実行できます。
この章では、外部コマンドを実行し、子供とのコミュニケーションを作成し、子供を終了する方法を示します。ポイントは、ノードプロセス以外で一連のタスクを完了する方法をアイデアすることです。 外部コマンドを実行します 外部シェルコマンドまたは実行可能ファイルを実行する必要がある場合は、child_processモジュールを使用して次のようにインポートできます。 コードコピーは次のとおりです。 var child_process = require( 'child_process') 次に、モジュールのexec関数を使用して、外部コマンドを実行できます。 コードコピーは次のとおりです。 var exec = child_process.exec; exec(command、callback); execの最初のパラメーターは、実行する準備をしているシェルコマンド文字列であり、2番目のパラメーターはコールバック関数です。このコールバック関数は、execが外部コマンドの実行を終了した場合、またはエラーが発生したときに呼び出されます。コールバック関数には3つのパラメーターがあります。エラー、stdout、stderr、次の例を参照してください。 コードコピーは次のとおりです。 exec( 'ls'、function(err、stdout、stderr){ //翻訳者のメモ:Windowsを使用する場合、dirなどのWindowsコマンドに変更できます。後で繰り返しません。 }); エラーが発生した場合、最初のパラメーターはエラークラスのインスタンスになります。最初のパラメーターにエラーが含まれていない場合、2番目のパラメーターstdoutにはコマンドの標準出力が含まれます。最後のパラメーターには、コマンド関連のエラー出力が含まれます。 リスト8-1は、外部コマンドを実行するより複雑な例を示しています リスト8-1:外部コマンドを実行する(ソースコード:第8章/01_external_command.js) コードコピーは次のとおりです。 // child_processモジュールのexec関数をインポートします var exec = require( 'child_process')。exec; //「cat *.js | wc -l」コマンドを呼び出します exec( 'cat *.js | wc l'、function(err、stdout、stderr){// line 4 //コマンドが終了するか、呼び出しが失敗します if(err){ //外部プロセスの開始に失敗しました console.log( 'child_process exit、エラーコードは次のとおりです。'、err.code); 戻る; } } 4行目では、execの最初のパラメーターとして「cat *.js | wc -l」を渡します。シェルで使用したコマンドがOKである限り、他のコマンドを試すこともできます。 次に、コールバック関数を2番目のパラメーターとして使用します。これは、エラーが発生したときに呼び出されるか、子プロセスが終了します。 コールバック関数の前に3番目のオプションパラメーターを渡すこともできます。これには、次のような構成オプションが含まれます。 コードコピーは次のとおりです。 var exec = require( 'child_process')。exec; var options = { タイムアウト:1000、 KillSignal:「Sigkill」 }; exec( 'cat *.js | wc l'、options、function(err、stdout、stderr){ //… }); 使用できるパラメーターは次のとおりです。 1.CWD-現在のディレクトリでは、現在の作業ディレクトリを指定できます。 2。エンコーディング - 子のプロセス出力コンテンツのエンコード形式、デフォルト値は「UTF8」、つまりUTF-8エンコードです。子プロセスの出力がUTF8ではない場合、このパラメーターを使用して設定できます。サポートされているエンコード形式は次のとおりです。 コードコピーは次のとおりです。 ascii UTF8 UCS2 base64 ノードでサポートされているこれらのエンコード形式について詳しく知りたい場合は、「バッファーを使用してバイナリデータの処理、エンコード、デコードを使用して第4章を参照してください。 1.TimeOut-コマンド実行タイムアウトはミリ秒で、デフォルトは0です。つまり、制限はありません。子プロセスが終了するまで待ちます。 2.maxbuffer- stdoutストリームとstderrストリームで許可される最大バイト数を指定します。最大値に達すると、子プロセスが殺されます。デフォルト値は200*1024です。 3。KillSignal-タイムアウトまたは出力キャッシュが最大値に達したときに子プロセスに送信される末端信号。デフォルト値は「Sigterm」であり、これは子プロセスに終了信号を送信します。これは通常、プロセスを整然と終了するために使用されます。 Sigterm信号を使用する場合、プロセスは、信号プロセッサを受信した後、信号プロセッサのデフォルトの動作を処理または書き換えることもできます。ターゲットプロセスが必要な場合は、他の信号を同時に渡すことができます(Sigusr1など)。また、オペレーティングシステムによって処理されるSigkill信号を送信し、子プロセスのクリーンアップ操作が実行されないように、子どものプロセスをすぐに終了させることもできます。 プロセスの終了をさらに制御する場合は、child_process.spawnコマンドを使用できます。 1.EVN-子プロセスに渡された環境変数を指定します。デフォルトはnullです。つまり、子どものプロセスは、作成される前にすべての親プロセスの環境変数を継承します。 注:KillSignalオプションを使用すると、ターゲットプロセスに文字列として信号を送信できます。ノードでは、信号は文字列として存在します。 UNIX信号と対応するデフォルト操作のリストは次のとおりです。 子プロセスに一連の拡張可能な親環境変数を提供することをお勧めします。 Process.ENVオブジェクトを直接変更すると、ノードプロセス内のすべてのモジュールの環境変数を変更すると、多くのトラブルが発生します。別の方法は、新しいオブジェクトを作成し、Process.ENVのすべてのパラメーターをコピーすることです。例8-2を参照してください。 リスト8-2:パラメーター化された環境変数を使用してコマンドを実行します(ソースコード:第8章/02_vars_augment.js) コードコピーは次のとおりです。 var env = process.env、 varname、 envcopy = {}、 exec = require( 'child_process')。exec; // process.envをenvcopyにコピーします for(vaname in ev){ envcopy [varname] = env [varname]; } //いくつかのカスタム変数を設定します envcopy ['custom env var1'] = 'ある値'; envcopy ['custom env var2'] = 'その他の値'; // process.envおよびカスタム変数を使用してコマンドを実行します exec( 'ls la'、{env:envcopy}、function(err、stdout、stderr){ if(err){ERRをスロー; } console.log( 'stdout:'、stdout); console.log( 'stderr:'、stderr); } 上記の例では、環境変数を保存するためにEnvcopy変数が作成されます。まず、Process.ENVからノードプロセスの環境変数をコピーし、変更する必要がある環境変数を追加または置き換え、最後にExec関数に環境変数パラメーターとしてEnvcopyを渡し、外部コマンドを実行します。 環境変数はオペレーティングシステムを介してプロセス間で渡され、あらゆるタイプの環境変数値が弦として子プロセスに到達することに注意してください。たとえば、親のプロセスが環境変数として123番を取得した場合、子プロセスは「123」を文字列として受け取ります。 次の例では、同じディレクトリで2つのノードスクリプトが作成されます:parent.jsとchild.js。最初のスクリプトは2番目のスクリプトを呼び出します。これら2つのファイルを作成しましょう。 リスト8-3:親プロセスは環境変数を設定します(第8章/03_environment_number_parent.js) コードコピーは次のとおりです。 var exec = require( 'child_process')。exec; exec( 'node child.js'、{env:{number:123}}、function(err、stdout、stderr){ if(err){ERRをスロー; } console.log( 'stdout:/n'、stdout); console.log( 'stderr:/n'、stderr); }); このコードをparent.jsに保存します。以下は、子プロセスのソースコードであり、それらをchild.jsに保存します(例8-4を参照) 例8-4:サブプロセスは環境変数を解析します(第8章/04_environment_number_child.js) コードコピーは次のとおりです。 var number = process.env.number; console.log(typeof(number)); //→「文字列」 number = parseint(number、10); console.log(typeof(number)); //→「番号」 このファイルをchild.jsとして保存した後、このディレクトリで次のコマンドを実行できます。 コードコピーは次のとおりです。 $ node parent.js 次の出力が表示されます。 コードコピーは次のとおりです。 sdtou: 弦 番号 stderr: ご覧のとおり、親プロセスは数値環境変数を渡しますが、子プロセスはそれを文字列として受け取り(出力の2行目を参照)、3番目の行では文字列を数値に解析します。 子プロセスを生成します ご覧のとおり、child_process.exec()関数を使用して外部プロセスを開始し、プロセスの最後にコールバック関数を呼び出すことができます。これは非常に簡単に使用できますが、いくつかの欠点があります。 1.コマンドラインパラメーターと環境変数を使用することに加えて、Exec()は子プロセスと通信できません。 2。子プロセスの出力はキャッシュされているので、ストリーミングすることはできません。 幸いなことに、NodeのChild_Processモジュールにより、より細かい粒度が子cildプロセスの開始、停止、およびその他の従来の操作を制御できます。アプリケーションで新しい子プロセスを開始できます。ノードは双方向通信チャネルを提供し、親と子のプロセスが互いに文字列データを送信および受信できるようにします。親プロセスは、子プロセスの管理操作を備え、子プロセスに信号を送信し、子どものプロセスを強制的に閉じることもできます。 子プロセスを作成します child_process.spawn関数を使用して、新しい子プロセスを作成できます。例8-5を参照してください。 例8-5:子プロセスを生成します。 (第8章/05_spawning_child.js) コードコピーは次のとおりです。 // child_processモジュールのスポーン関数をインポートします 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ファイル(存在する場合)を監視し、すべての新しいデータを出力し、stdout標準出力ストリームに出力します。スポーン関数は、実際のプロセスのアクセスインターフェイスをカプセル化するポインターオブジェクトであるChildProcessオブジェクトを返します。この例では、この新しい記述子を子と呼ばれる変数に割り当てます。 子プロセスのデータを聞いてください stdout属性を含む子プロセスハンドルは、子プロセスの標準出力をストリームオブジェクトとして取得します。このストリームオブジェクトのデータイベントをバインドできます。これにより、データブロックが利用可能になるといつでも、対応するコールバック関数が呼び出されます。次の例を参照してください。 コードコピーは次のとおりです。 //子プロセスの出力をコンソールに印刷します child.stdout.on( 'data'、function(data){ console.log( 'テール出力:' +データ); }); 子プロセスがデータを標準の出力stdoutに出力するたびに、親プロセスに通知され、データをコンソールに印刷します。 標準出力に加えて、プロセスには別のデフォルトの出力ストリーム、つまり標準エラーストリームがあります。これは通常、エラー情報を出力するために使用されます。 この例では、/var/log/system.logファイルが存在しない場合、テールプロセスは次のようなメッセージを出力します。 STDERRストリームを聞くことにより、このエラーが発生したときに親プロセスが通知されます。 親プロセスは、次のような標準のエラーストリームを聞くことができます。 コードコピーは次のとおりです。 child.stderr.on( 'data'、function(data){ console.log( 'テールエラー出力:'、データ); }); stderrプロパティは、stdoutのように、読み取り専用のストリームでもあります。子プロセスがデータを標準エラーストリームに出力すると、親プロセスに通知され、データが出力されます。 子プロセスにデータを送信します 子プロセスの出力ストリームからデータを受信することに加えて、親プロセスは、ChildPoces.stdinプロパティを介して子プロセスの標準入力にデータを書き込み、子プロセスにデータを送信することもできます。 子プロセスは、Process.stdin Readのみのストリームを介して標準の入力データを聞くことができますが、デフォルトでは一時停止された状態にあるため、最初に標準の入力ストリームを再開する必要があることに注意してください。 例8-6は、次の関数を含むプログラムを作成します。 1.+1アプリケーション:標準入力から整数を受信してから追加してから、標準の出力ストリームに追加した後に結果を出力できる簡単なアプリケーション。簡単なコンピューティングサービスとして、このアプリケーションは、特定のタスクを実行できる外部サービスとしてノードプロセスをシミュレートします。 2。+1アプリケーションのクライアントをテストし、ランダム整数を送信してから、結果を出力します。ノードプロセスが子プロセスを生成する方法を実証し、特定のタスクを実行できるようにするために使用されます。 例8-6の次のコードを使用して、plus_one.jsという名前のファイルを作成します。 例8-6:+1アプリケーション(第8章/06_PLUS_ONE.JS) コードコピーは次のとおりです。 //デフォルトで一時停止される標準の入力ストリームを復元する process.stdin.resume(); process.stdin.on( 'data'、function(data){ var番号; 試す { //入力データを整数に解析します number = parseint(data.toString()、10); // +1 番号 += 1; //出力結果 process.stdout.write(number + "/n"); } catch(err){ process.stderr.write(err.message + "/n"); } }); 上記のコードでは、STDIN標準入力ストリームからのデータを待ちます。データが利用可能なときはいつでも、整数であると仮定して整数変数に解析し、1を追加して、結果を標準の出力ストリームに出力します。 次のコマンドを介してこのプログラムを実行できます。 コードコピーは次のとおりです。 $ node plus_one.js 実行後、プログラムは入力を待ち始めます。整数を入力してEnterを押すと、画面に表示される1を追加した後に数字が表示されます。 Ctrl-Cを押してプログラムを終了できます。 テストクライアント これで、以前の「+1アプリケーション」が提供するコンピューティングサービスを使用するノードプロセスを作成する必要があります。 まず、Plus_one_test.jsという名前のファイルを作成します。例8-7を参照してください。 例8-7:テスト+1アプリケーション(第8章/07_PLUS_ONE_TEST.js) コードコピーは次のとおりです。 var spawn = require( 'child_process')。spawn; // +1アプリケーションを実行するために子プロセスを生成します var child = spawn( 'node'、['plus_one.js']); //毎秒関数を呼び出します setInterval(function(){ // 10.000未満の乱数を作成します var number = math.floor(math.random() * 10000); //その番号を子プロセスに送信します。 child.stdin.write(number + "/n"); //子プロセスから応答を取得し、それを印刷してください。 child.stdout.once( 'data'、function(data){ console.log( '子供は' + number + 'に答えました:' + data); }); }、1000); child.stderr.on( 'data'、function(data){ process.stdout.write(data); }); 最初の行から4行目まで、子のプロセスが「+1アプリケーション」を実行するために開始され、次の操作がsetinterval関数を使用して毎秒実行されます。 1. 10000未満の新しい乱数を作成します 2。この数字を子供のプロセスに文字列として渡す 3。子のプロセスが文字列に返信するのを待ちます 4.一度に1つの番号だけを受け取る必要があるため、child.stdout.onの代わりにchild.stdout.onceを使用する必要があります。後者を使用すると、データイベントのコールバック関数が1秒ごとに登録されます。子プロセスのstdoutがデータを受信すると、登録された各コールバック関数が実行されます。このようにして、同じ計算結果が複数回出力されることがわかります。この動作は明らかに間違っています。 子どものプロセスが終了したときに通知を受け取ります 子のプロセスが終了すると、出口イベントが解雇されます。例8-8はそれを聞く方法を示しています: 例8-8:子プロセスの出口イベントを聞く(第8章/09_listen_child_exit.js) コードコピーは次のとおりです。 var spawn = require( 'child_process')。spawn; //子のプロセスを生成して「LS -LA」コマンドを実行する var child = spawn( 'ls'、['-la']); child.stdout.on( 'data'、function(data){ console.log( '子からのデータ:' + data); }); //子プロセスが終了するとき: <strong> child.on( 'exit'、function(code){ console.log( 'code' + codeで終了した子プロセス); }); </strong> ブラックコードの最後の数行で、親プロセスは子どものプロセスの出口イベントを使用して、出口イベントを聴きます。イベントが発生すると、コンソールは対応する出力を表示します。子プロセスの出口コードは、最初のパラメーターとしてコールバック関数に渡されます。一部のプログラムは、非0の出口コードを使用して特定の障害状態を表します。たとえば、コマンド「ls al click filename.txt」を実行しようとすると、現在のディレクトリにはこのファイルがない場合は、値が1の出口コードが取得されます。例8-9を参照してください。 例8-9:子プロセスの出口コードを取得します(第8章/10_CHILD_EXIT_CODE.js) コードコピーは次のとおりです。 var spawn = require( 'child_process')。spawn; //子のプロセスを生成し、「ls dos_not_exist.txt」コマンドを実行します var child = spawn( 'ls'、['dos_not_exist.txt']); //子プロセスが終了するとき child.on( 'exit'、function(code){ console.log( 'code' + codeで終了した子プロセス); }); この例では、Exitイベントはコールバック関数をトリガーし、Child ProcessのExitコードを最初のパラメーターとして渡します。子どものプロセスが信号によって殺されて異常に終了する場合、例8-10のように、対応する信号コードは2番目のパラメーターとしてコールバック関数に渡されます。 リスト8-10:子プロセスの出口信号を取得します(第8章/11_CHILD_EXIT_SIGNAL.js) コードコピーは次のとおりです。 var spawn = require( 'child_process')。spawn; //チャイルドプロセスを生成し、「スリープ10」コマンドを実行します var child = spawn( 'sleep'、['10']); setimeout(function(){ child.kill(); }、1000); child.on( 'exit'、function(code、signal){ if(code){ console.log( 'code' + codeで終了した子プロセス); } else if(signal){ console.log( '信号のために終了した子プロセス' +信号); } }); この例では、子どものプロセスが10秒睡眠を実行するために開始されますが、10秒前にシグキル信号が子プロセスに送信され、次の出力が生じます。 コードコピーは次のとおりです。 Signal Sigtermのために児童プロセスが終了しました 信号を送信して、プロセスを殺します このセクションでは、信号を使用してサブプロセスを管理する方法を学びます。信号は、親のプロセスが子供とコミュニケーションを取り、子供を殺すための簡単な方法です。 異なる信号コードは異なる意味を表しており、多くの信号があり、その一部はプロセスを殺すために使用される最も一般的なものです。プロセスが処理方法がわからないという信号を受信した場合、プログラムは例外によって中断されます。一部の信号はサブプロセスによって処理されますが、他の信号はオペレーティングシステムでのみ処理できます。 一般に、child.killメソッドを使用して子プロセスに信号を送信し、デフォルトでSigterm信号を送信できます。 コードコピーは次のとおりです。 var spawn = require( 'child_process')。spawn; var child = spawn( 'sleep'、['10']); setimeout(function(){ child.kill(); }、1000); また、キルメソッドの唯一のパラメーターとして信号を識別する文字列を渡すことにより、特定の信号を送信することもできます。 コードコピーは次のとおりです。 child.kill( 'sigusr2'); この方法の名前はキルですが、送信された信号は必ずしも子プロセスを殺すわけではないことに注意する必要があります。子が信号を処理した場合、デフォルトの信号動作が上書きされました。ノードで記述されたサブプロセスは、信号プロセッサの定義を次のように書き換えることができます。 コードコピーは次のとおりです。 process.on( 'sigusr2'、function(){ console.log( 'sigusr2信号を取得しました'); }); これで、SIGUSR2信号プロセッサを定義しました。プロセスが再度Sigusr2信号を受信すると、殺されることはありませんが、代わりに「sigusr2信号を取得した」文を出力します。このメカニズムを使用して、子どものプロセスと通信する簡単な方法を設計したり、コマンドすることもできます。標準入力を使用するほどリッチではありませんが、この方法ははるかに単純です。 まとめ この章では、child_process.execメソッドを使用して外部コマンドを実行することを学びました。これにより、コマンドラインパラメーターを使用する代わりに、パラメーターを子プロセスに渡すことができますが、代わりに環境変数を定義できます。 また、child_process.spawnメソッドを呼び出して外部コマンドを呼び出すことにより、子プロセスを生成する方法も学びました。このようにして、入力ストリームと出力ストリームを使用して子供のプロセスと通信したり、シグナルを使用して子供のプロセスと通信したり、プロセスを殺したりできます。