signalの処理について。

600 views
Skip to first unread message

Taro YACHI

unread,
Apr 26, 2012, 10:23:19 AM4/26/12
to nodejs_jp
やちといいます。

環境:
FreeBSD 8.2R p3
node v0.6.14

SIGINTとかのsignalをうけとりたいと思い、下記のようなcodeを書きました。

tp.js
-------------------------------------------------------
process.on('SIGUSR1', function (){
console.log("usr1.");
process.exit(100);
});
process.on('SIGINT', function (){
console.log("interrupted.");
process.exit(200);
});
process.on('exit', function (){
console.log("exit.");
});

var c = 0;
while(true){
console.log("foo" + c);
c++;
// if (c == 10000) {
// process.exit(10);
// }
}
-------------------------------------------------------

で、$ node tp.js として、実行。他のterminalから$ killall -USR1 node としても、割り込みにより
console.logでの文言を出力したり、process.exit()によって止めたり出来ません。また、nodeを実行したterminalで
CTRL-Cをしても止まりません。
SIGINTのprocess.on()を削ると、CTRL-Cで止まるので、trapは出来ているような気がしています。が、
console.llogしている文言はいずれも出力されません。

なにか、指摘をお願いします。

Shigeki Ohtsu

unread,
Apr 26, 2012, 10:54:43 AM4/26/12
to node...@googlegroups.com
大津です。

while(true) {} が終了しないので次のイベントループに回らないからだと思い
ます。
(正確には tp.js の読み込みで1回分イベントループが回っていますが)

while(true) で回すのではなく process.nextTick() で再帰的にカウンター
を回せば、おそらくシグナルウォッチャーのコールバックを受けることが
できるはずです。

以下を試してみたらいかがでしょうか?

-------------------------------------------------------
process.on('SIGUSR1', function (){

console.log("usr1.");
process.exit(100);
});
process.on('SIGINT', function (){
console.log("interrupted.");
process.exit(200);
});
process.on('exit', function (){
console.log("exit.");
});

var c = 0;
function output() {
process.nextTick(function() {
console.log("foo" + c);
c++;
// if (c == 10000) {
// process.exit(10);
// }
output();
});
}
output();

-------------------------------------------------------

Taro YACHI

unread,
Apr 26, 2012, 11:55:00 AM4/26/12
to node...@googlegroups.com
やちです。

提示していただいたcodeでこちらの思い通りの動作になりました。
実行context?をnodeに戻さないといけないのは、考えてみればあたりまえなんですけど、気がつきませんでした。

ありがとうございました。

2012/4/26 Shigeki Ohtsu <oh...@iij.ad.jp>:

Shigeki Ohtsu

unread,
Apr 26, 2012, 10:25:32 PM4/26/12
to node...@googlegroups.com
大津です。

解決して何よりです。

ついでにですが今回の件は違う面からみるとある意味教育的な
ところがありまして、現象として

「今回の whiile(true) {} などのように node のプロセスCPU を食い
つぶす処理を行うと、見た目 Node がハングしたようになる。」

ということだったんじゃないかと思います。

これは昨年話題となった "Node.js is Cancer"
http://teddziuba.com/2011/10/node-js-is-cancer.html

「フィボナッチ数を計算して出力するだけなのに Node.js はこんな
に遅くて使えない。」

といったことと同じで、

「プロセスのCPUを使う重い処理は(イベントループに影響を与えるので)
Node には向かない」

といわれる所以です。(先のフィボナッチ処理も process.nextTick() の再帰
処理で実装すると非常に高速になりました。)

でも、重い処理だけど iterative にできない Node で使うにはどうしたら?
というケースもあるかもしれません。
そんな時は処理を外出しするのも一つの方法です。

今回の例を cluster で外出しするとこんな感じです。重い処理を worker に
やらせるので master の方は signal を受けて処理するだけになります。

node-v0.8 では Cluster 関連の API、イベントが多く追加され、これまで以上
に柔軟な worker 制御が可能になります。近日のリリースを楽しみにして下さい。

------------------------------------------------------------
var cluster = require('cluster');

if (cluster.isMaster) {

var worker = cluster.fork();

process.on('SIGUSR1', function (){
process.kill(worker.pid);
console.log("usr1.");
process.exit(100);
});
process.on('SIGINT', function (){
process.kill(worker.pid);
console.log("interrupted.");
process.exit(200);
});
process.on('exit', function (){
console.log("exit.");
});
cluster.on('death', function(w) {
process.exit(10);
});

} else {

Shigeki Ohtsu

unread,
May 28, 2012, 9:02:32 PM5/28/12
to node...@googlegroups.com
大津です。

> while(true) で回すのではなく process.nextTick() で再帰的にカウンター
> を回せば、おそらくシグナルウォッチャーのコールバックを受けることが
> できるはずです。

こんなこと書いちゃって process.nextTick() の再帰処理を使うコードが増えて
しまっているかもしれないので、かなり早めに注意事項をお知らせしておきます。

一昨日 isaacs から process.nextTick() の動作仕様を変更する提案がなされま
した。

"process.nextTick semantics"
https://groups.google.com/group/nodejs-dev/browse_thread/thread/ef10d80af0db9bc?hl=ja

"process.nextTick should be cleared after every v8 invocation"
https://github.com/joyent/node/issues/3335

要点としては、

・process.nextTick() でイベントハンドラを追加するのはよくやることだけど
次のイベントループでハンドラが登録されるまでの間にイベントが発生したりす
ると取りこぼしが起きてしまう。
・次のイベントが発生する前に確実にハンドラを登録をするために、V8でJSを実
行した直後に process.nextTick() に登録された関数(nextTickQueue)を全部実
行するようにしたい。
・再帰処理とかの展開もそこで行うので次のようなコードでは setTimeout() は
起動しなくなるよ。(nextTick の再帰ループがずっと動く event loop
starvation と言ってます。)

setTimeout(function() {
console.log('timeout');
});
process.nextTick(function f() {
process.nextTick(f);
}, 1000);

この動作変更により先日例示した process.nextTick() の再帰処理を使うコード
が将来的に動かなくなる可能性があります。(動いても動作挙動が変わるかもし
れません。)

まだ議論が始まったばかりですが、node-v0.8 までは現状の挙動のままにするけ
ど node-v0.9からは変えるかもという話です。
今もし process.nextTick() の再帰処理の実装をされている/しようという方が
いらっしゃいましたら今後変わるかもということを頭の隅に入れておいてください。

PRでこのMLで出た例を紹介しましたが、そもそも CPU busy なループを回すのが
bad idea だとのコメントが出ています。
個人的には process.nextTick() の再帰は使うことはないのですが、もし何か
ユースケースなど持っていて今後使えないとホント困るというようなことがあれ
ばPRで紹介しますので、このメールにでも返答してくれればと思います。


Toshihiro Shimizu

unread,
May 28, 2012, 10:41:32 PM5/28/12
to node...@googlegroups.com
mesoです。

おお、情報ありがとうございます。
僕は再帰的に書いてることある気がするなぁ。
ちょっと見返してみます。

2012/5/29 Shigeki Ohtsu <oh...@iij.ad.jp>:
--
Toshihiro Shimizu / @meso
Reply all
Reply to author
Forward
0 new messages