「ウェブの歴史は人類の歴史の繰り返し」という観点から色々勉強しています。commentsといっても、きちんとした検証をしたわけではないので、あくまで「こういう環境でこういう事をやるとこうなる」という参考程度のものと理解してい頂ければ幸いです。Linux上でプロセスは同時に何個起動できるのか数年前にC10K問題が流行りました。これは、簡単に言うと、万のオーダーでプロセスを立ち上げる事になると、現状のOSではそれを想定した設計になっていないためまともに動かなくなる、といった問題でした。「10万プロセス位を同時に立ち上げてみて、どうなるか試してみようぜ!」と思い、会社のエンジニアと一緒に試してみました。検証環境は、メモリ48GでCPUはHyperThreading込で24コアです。そこで動いていたOSはDebianでLinuxカーネル2.6.32だったので、とりあえずそのカーネルで試してみました。やりたい事として、まずは3万プロセスを同時に起動させるそれぞれのプロセス上で定期的にコンテキストスイッチや割り込みを発生させるどういうことになるのだろうか?ということにしました。3万プロセスを立ち上げるための準備そこで、簡単に以下のようなツールを作りました。"hoge";}まずは、適当に3秒の範囲内でランダムの値でスリープし、定期的に/dev/nullに文字列を書き込むスクリプトを作りました。次に、以下のようなラッパースクリプトを書きました。&`;}sleepしながら定期的に書き込みを行うスクリプトを、1万個forkさせるように実装しました。これによって、forkされたプロセスは各自ランダムにsleepしつつ、コンテキストスイッチが定期的に発生する事になります。このツールをforkしたい数だけ実行しました。今回は3万が目標なので、3回実行することになります。(4194304)を書き込みました。/proc/sys/kernel/pid_max色々試してみた結果、この値が上限のようです。3万プロセスを立ち上げてみたではまず、3万プロセスを立ち上げてみました。vmstatで1秒間のコンテキストスイッチの数と割り込み回数に注目しました。1秒間のコンテキストスイッチ数:約9万1秒間の割り込み回数:約7万おおよそ上記のような値になりました。メモリやCPUのリソースはまだまだスカスカでした。動かしていると、以下のようなメッセージが急に出力され出しました。・おおー、これはなんだー。Excepution)について。これは、簡単に言うとx86系のCPUの自己診断機能で、ハードウェアの障害を事前に検知してくれる機能のようです。「HWの回復不能なエラーをCPUが検知する機能」なようで、はて、なんでこんなエラーが出るんだろうと疑問に思いました。カーネルの海へでは早速カーネルのコードリーディングに入ります。logged」というログに注目して、カーネルの海に飛び込みました。GLOBALを完備している僕に死角はありません。いうことで、カーネルはとりあえず3.3のカーネルを見てみてました。いう実装を見つける事ができます。GLOBALであれば一瞬です。2)という関数があります。中に定義されていることがわかります。}上記コードの20行目から26行目に渡って書かれており、ratelimit_state構造体であるnameに、引数で渡したinterva_init、つまりは60*HZと、burst_init、つまりは2を代入しています。ここで、HZとはCPUが1秒間に発生するタイマ割り込みの回数で、60*HZというのは、1分間で発生するタイマ割り込みの回数になります。ここで、カーネルにおいては、様々なタイマ処理にjiffiesとよばれるグローバル変数が利用されています。jiffiesはカーネルが起動してから、発生するタイマ割り込みのタイミングでインクリメントされる変数です。このjiffiesの値を利用することで、例えば60秒という時間は、jiffiesの値が60*HZだけ増えていることで判断する事ができます。そのような前提知識を持って、さらにソースを見ていきます。mce_notify_irq()に戻って、DEFINE_RATELIMIT_STATEの続きを見ていきます。EXPORT_SYMBOL_GPL(mce_notify_irq);すると、1228行目の__ratelimit(&ratelimit)がtrueを返している場合に、MCEのHW_ERRのログが出力されているようです。__ratelimit()を見ていきます。すると、/usr/local/src/linux-3.3/include/linux/ratelimit.hにおいてマクロで以下のように定義されています。__func__)さらに、___ratelimit()を追います。/usr/local/src/linux-3.3/lib/ratelimit.cの中に___ratelimit()がありました。EXPORT_SYMBOL(___ratelimit);この関数がtrueを返す状況は2つあるようです。33行目でratelimit_stateのintervalが偽の場合です。これは上述したDEFINE_RATELIMIT_STATE()によって、60*HZの値だけjiffiesのタイマの単位で定義されているはずです。そのため、trueを返す状況としては、42行目から58行目の実装によるものだと考えられます。}まず、rs->beginは初期化時に0が代入されているため、現状のjiffiesの値が入ります。rs->interval)によって評価します。この関数を見ていきます。/usr/local/src/linux-3.3/include/linux/jiffies.hでマクロとして以下のように定義されています。a)を見ていくと、同じ/usr/local/src/linux-3.3/include/linux/jiffies.hで以下のようにマクロで定義されているようです。rs->interval)は現在のjiffiesの値より、rs->beginに入れた時のjiffiesの値と60*HZしたタイマ割り込み数を足した値が少ない場合にtrueが返る事がわかります。その場合は、rs->begin、rs->printedにそれぞれ0が代入されます。rs->printed)において、rs->burstは上述したDEFINE_RATELIMIT_STATE()において2が代入されているため真となり、返り値であるretに1が入ります。最後にもう一度mce_notify_irq()のコードを以下に示します。EXPORT_SYMBOL_GPL(mce_notify_irq);上記から、1228行目の__ratelimit()はtrueとなり、エラーログが出力されるわけです。考察このように、万のオーダーでプロセスを生成して数万のコンテキストスイッチや割り込み処理を発生させると、リソースの空き状況によらず以下のような問題が発生する事がわかりました。ExceptionによってCPU自己診断機能がエラーを出すエラー内容はCPUが割り込み待ちに60秒以上かかってしまう状況が発生?恐らくCPUのBUSY状態が長すぎる事を示すエラーというような予想がつきます。誰かこの詳細が分かる方はいらっしゃいますか?是非ともご教授頂けるとうれしいです。中に定義されているデフォルトのratelimitのintervalやbusrt値に置き換えて再チェックしていく実装になっている事がわかりました。10まず、この状況がどれほどまずいのかが分かっていません。MCEがエラーを出しているんだから良くない事が起きているんだとは予想できます。この状況で他のプロセスを立ち上げたり処理したりしても、特にOS上でおかしな事が起きているようにも思えませんでした。結論として、万のオーダーでプロセスを立ち上げる用途では、インターバルの値をもっと長くしてあげる必要があるのかもしれませんが、全く自信はありません。MCEが反応しだしたということは、「僕はCPUですが、割り込みとかコンテキストスイッチ多過ぎでもう無理っす」というメッセージなのだろうか。割には、レスポンスは結構ぴんぴんしていました。ちなみにsleepしてwriteするプロセスをC言語で実装すると、10万プロセス以上を同時に実行できて、1秒間のコンテキストスイッチ数を15万、1秒間の割り込み数を7万、にしても上記のエラーはでませんでしたし、結構さくさく動いていました。プロセス単位でのリソースも関連しているのかもしれませんね。フォローアップ(追記:2012年5月17日22時)twitterの皆様に色々と感想やご指摘を頂いて、非常に参考になるものばかりでしたので、上記のmceの挙動について現状の解釈を追記します。さんの指摘がズバリだと思いました。60秒なのは表示の抑制だけで、エラー(mce_need_notifyに値がセット)は別条件な感じにも見えるような。さんの解釈が非常に参考になりました。来た時点で、エラーだよってことなのでもっと上を見ないと。2012そうですね、確かにratelimitに来た時点でmissedになろうとprintedになろうといずれかのエラーが出力されてしまいます。真になってログがでるので、このフラグを立てる人を探す必要があるのかな。2012非常に分かりやすい!これを聞いてコードを読むと、まさのそのとおりだと思いました。結局大元のエラー出力の関数を調べていくと以下のようにmce_log()にたどり着きます。2012で、最終的にはkernelがエラーを検知するのではなく、CPUの機構で検知しているように思われます。CPUがエラーを検知するとどこか(CPU内?)にログを書き込み、それを割り込みで通知してきて最終的にmce_log()が呼ばれるようです。カーネルではエラーの検知はしてないようです。CPUのドキュメントを見ていく必要がありそうです。ちなみに、そういう調査も含めて今後は以下のような観点でも検証していきたいと考えています。マルチプロセスだとコンテキストスイッチ毎にTLBがフラッシュされるのも、あれ程のプロセス数だとCPUにどういう影響与えるのか、を含めておってみたいと思っています。2012続きやりましょう。各プロセスでTCPコネクションもはらせたいですね。なるとfdやらソケットI/Oの問題とか色々でてきそうですけど。2012以上、簡単ではありますが、今回のmce_notify_irq()周りの解釈はこの程度に留めておこうと思います。ログを1分間に2回に抑えるためのロジックに思えます。logged”のログが出力されるのかと。mce_log()が呼ばれるとフラグがたちますね。いうことで、mce_log()が呼ばれる条件を追うのがよさそうです。shmoromoさんtwitterでは、ありがとうございました。僕自身が完全にわかっているわけではないので断言はできませんが、shmorimoさんの解釈で正しいと思われます。本エントリに引用させて頂きますね。Commentコメントを投稿するにはログインしてください。