状況から見ると、どうやらアプリケーションが悪いわけではなさそうです。ここからはいよいよ、ソフトウェアの下位レイヤであるOS部分の解析に入ります。LinuxのオープンソースOSたる長所を生かして、ソースコードを読みつつ解析していくことになりそうです。
……とはいえ、OS以下のレイヤをある程度分類し、事前に要因を想定しておかなければ、やみくもにソースコードを読むことになってしまいます。
「アプリケーションは悪くない」ことを前提とすると、残りのレイヤは以下の3つに絞られます。
まず1と2を切り分けるために、「マジックSysRqキー」(以下、SysRqキー)でスタックトレースを取得してみましょう。
ではここから、実際にスタックトレースを出力するための手順を紹介します。
1. SysRqが有効かどうかを確認
まずは現在のサーバで、SysRqキーが有効になっているかどうかを以下のようにして確かめます。kernel.sysrqが0になっている場合は無効になっています。
# sysctl -a | grep sysrq |
2. sysctlコマンドでSysRqキーを有効にする
# sysctl -w kernel.sysrq=1 |
3. 実際にSysRqキーを試してみる
SysRqキーは基本的に、サーバに直接接続されているコンソール上で実行しますので、まずはそこまで移動しましょう。CUIの場合(ランレベルが3など)は問題ありませんが、GUIを利用している(ランレベルが5)場合は、[Ctrl]+[Alt]+[F1]などで端末に切り替えましょう。
端末に移動した状態で、[Alt]+[SysRq]+hとキーボードを押してみてください。以下のように出力されるはずです。
SysRq : HELP : loglevel0-8 reBoot Crash tErm kIll saK |
これは、SysRqキーで実行できるコマンド群を示しています。SysRqキーと大文字部分のキーを組み合わせて押すことで、コマンドが実行できます。
4.スタックトレースを出力する
いよいよスタックトレースを取得します。3.で出力されていたコマンド群のうち、「showTasks」というのが全プロセスのスタックトレースを出力するコマンドです。まず「つかまえたい事象が起こっている」のを確認したうえで、[Alt]+[Sys Rq]+Tと押してみます。これで、全プロセスのスタックトレースが出力されたはずです。
この出力は、RHELの場合はデフォルトで/var/log/messageにも出力されます。ここからお望みのプロセスを探し出せば、現在どこで処理が止まっているかが分かります。
例えば、今回の対象プログラム名が「test」だった場合のスタックトレースは、以下のような出力になります。
1:test S 0000000000000000 0 19127 18965 (NOTLB) |
スタックトレースの細かい読み方についてはここでは割愛しますが、12行目の赤字部分に注目してください。
sys_recvfromというのは、カーネル内にあるrecvfrom()の実体です。sys_recvfrom+182というのは、sys_recvfromの途中で止まっていることを示しています。何度か取得してみても同じ結果ですので、recvfrom()の処理のうち、カーネル内の処理でシステムが止まっていると判断できそうです。
スタックトレースを見ることにより、カーネルのrecvfrom()内で処理が停止していることが分かりました。
次に、カーネルとハードウェアの切り分けに入ります。取得したスタックトレースを参考にカーネルのソースコードを調査してみると、どうやらrecvfrom()の処理ルーチンのうち、UDPのパケットを待ち受ける処理の途中で止まってしまうようです。
ところがアプリケーションの作りとしては、select()でパケットが到着したことを確認したうえでrecvfrom()を発行しています。本来ならば、パケットの待ち受け処理で止まるはずがありません。
しかし、このような処理になるケースが1つだけ考えられます。
recvfrom()は、その延長上でパケットのチェックサムを調べる(注3)仕組みになっています。ここでエラーが発生すると、パケットを破棄したうえで次のパケットを待ち受けるという処理に入ってしまうのです(注4)
今回の実装では、パケット受信処理は以下のように進みます。
6のrecvfrom()による受信処理は、単一のソケットに対して行われます。もしこの処理中にチェックサムエラーが出ると、当該ソケットに対するパケットを待ち受けることになってしまい、ほかのソケットに対する受信処理ができなくなってしまいます。
どうやら、これは今回の状況とかなり符合しそうです。この事象の原因はサーバではなくクライアント、それもハードウェアに問題がある可能性が出てきたということになります。
次に、パケットのチェックサムエラーが発生しているかどうかを調べる必要がありますが、それに関しては簡単な方法があります。Linuxは、SNMPアプリケーションのために/proc/net/snmpというエントリを用意しています。この中にある「InErrors」という値が、まさにこのチェックサムエラーが出た数を示しています。
この値を顧客に調べてもらったところ、やはりチェックサムエラーがカウントされているとのこと。従って、端末から不正なパケットが送られてきたことが、今回の事象のきっかけとなったことに間違いなさそうです。
顧客によると、その後の追加調査で、ある特定の端末が、とある時点を境に、エラーパケットしか送信しなくなるということが分かりました。しかも、この端末はリセットすると正常な状態に戻るため、それが再現条件を特定するのが難しい一因となっていたようです。
注3:チェックサムについては、http://www.atmarkit.co.jp/fwin2k/network/baswinlan010/baswinlan010_02.htmlを参考にしてください
注4:最近のNICの中には、受信時にハードウェアレベルでチェックサムを検査してくれるものもあるので、それを利用している場合には発生しません
Copyright © ITmedia, Inc. All Rights Reserved.